Squash the f-docker-hostmachine branch.
Initial work commands/up: make sure all names to with_target_vms are strings providers/docker: create a docker host VM if needed providers/docker: executor abstraction for driver to eventually support remote providers/docker: vagrant executor providers/docker: support creating the machine providers/docker: status works if host VM is gone providers/docker: use start fence to get real docker output core: Call preserves stack ordering core: support Message post option providers/docker: Guard some features with HasSSH checks providers/docker: much better messaging around create/destroy providers/docker: output the container ID on create providers/docker: copy the hostmachine Vagrantfile to the data dir providers/docker: should make host machine before any up action providers/docker: HandleBox before the host machine providers/virtualbox: functional_vboxsf to disable vboxsf providers/virtualbox: synced folder usable method should take 2 args providers/docker: default machine name to :default
This commit is contained in:
parent
d42d62ead1
commit
8c7ab333a0
|
@ -46,15 +46,14 @@ module Vagrant
|
||||||
builder = Builder.new
|
builder = Builder.new
|
||||||
@block.call(new_env, builder)
|
@block.call(new_env, builder)
|
||||||
|
|
||||||
# Run the result with our new environment
|
# Append our own app onto the builder so we slide the new
|
||||||
|
# stack into our own chain...
|
||||||
|
builder.use @app
|
||||||
@child_app = builder.to_app(new_env)
|
@child_app = builder.to_app(new_env)
|
||||||
final_env = runner.run(@child_app, new_env)
|
final_env = runner.run(@child_app, new_env)
|
||||||
|
|
||||||
# Merge the environment into our original environment
|
# Merge the environment into our original environment
|
||||||
env.merge!(final_env)
|
env.merge!(final_env)
|
||||||
|
|
||||||
# Call the next step using our final environment
|
|
||||||
@app.call(env)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def recover(env)
|
def recover(env)
|
||||||
|
|
|
@ -6,11 +6,19 @@ module Vagrant
|
||||||
def initialize(app, env, message, **opts)
|
def initialize(app, env, message, **opts)
|
||||||
@app = app
|
@app = app
|
||||||
@message = message
|
@message = message
|
||||||
|
@opts = opts
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
env[:ui].output(@message)
|
if !@opts[:post]
|
||||||
|
env[:ui].output(@message)
|
||||||
|
end
|
||||||
|
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
|
|
||||||
|
if @opts[:post]
|
||||||
|
env[:ui].output(@message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -293,9 +293,12 @@ module Vagrant
|
||||||
# Fast-path if there is no prefix
|
# Fast-path if there is no prefix
|
||||||
return message if prefix.empty?
|
return message if prefix.empty?
|
||||||
|
|
||||||
|
target = @prefix
|
||||||
|
target = opts[:target] if opts.has_key?(:target)
|
||||||
|
|
||||||
# Otherwise, make sure to prefix every line properly
|
# Otherwise, make sure to prefix every line properly
|
||||||
message.split("\n").map do |line|
|
message.split("\n").map do |line|
|
||||||
"#{prefix}#{@prefix}: #{line}"
|
"#{prefix}#{target}: #{line}"
|
||||||
end.join("\n")
|
end.join("\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -60,7 +60,7 @@ module VagrantPlugins
|
||||||
if names.empty?
|
if names.empty?
|
||||||
@env.vagrantfile.machine_names_and_options.each do |n, o|
|
@env.vagrantfile.machine_names_and_options.each do |n, o|
|
||||||
o[:autostart] = true if !o.has_key?(:autostart)
|
o[:autostart] = true if !o.has_key?(:autostart)
|
||||||
names << n if o[:autostart]
|
names << n.to_s if o[:autostart]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,30 @@ module VagrantPlugins
|
||||||
def self.action_up
|
def self.action_up
|
||||||
Vagrant::Action::Builder.new.tap do |b|
|
Vagrant::Action::Builder.new.tap do |b|
|
||||||
b.use ConfigValidate
|
b.use ConfigValidate
|
||||||
|
b.use HandleBox
|
||||||
|
|
||||||
|
b.use Call, IsState, :host_state_unknown do |env, b2|
|
||||||
|
if env[:result]
|
||||||
|
b2.use HostMachine
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
b.use Call, IsState, :not_created do |env, b2|
|
b.use Call, IsState, :not_created do |env, b2|
|
||||||
# If the VM is NOT created yet, then do the setup steps
|
# If the VM is NOT created yet, then do the setup steps
|
||||||
if env[:result]
|
if env[:result]
|
||||||
b2.use HandleBox
|
|
||||||
b2.use EnvSet, :port_collision_repair => true
|
b2.use EnvSet, :port_collision_repair => true
|
||||||
b2.use HandleForwardedPortCollisions
|
b2.use HandleForwardedPortCollisions
|
||||||
b2.use Provision
|
|
||||||
|
b2.use Call, HasSSH do |env2, b3|
|
||||||
|
if env2[:result]
|
||||||
|
b3.use Provision
|
||||||
|
else
|
||||||
|
b3.use Message,
|
||||||
|
I18n.t("docker_provider.messages.provision_no_ssh"),
|
||||||
|
post: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
b2.use PrepareNFSValidIds
|
b2.use PrepareNFSValidIds
|
||||||
b2.use SyncedFolderCleanup
|
b2.use SyncedFolderCleanup
|
||||||
b2.use SyncedFolders
|
b2.use SyncedFolders
|
||||||
|
@ -61,6 +78,12 @@ module VagrantPlugins
|
||||||
# the virtual machine, gracefully or by force.
|
# the virtual machine, gracefully or by force.
|
||||||
def self.action_halt
|
def self.action_halt
|
||||||
Vagrant::Action::Builder.new.tap do |b|
|
Vagrant::Action::Builder.new.tap do |b|
|
||||||
|
b.use Call, IsState, :host_state_unknown do |env, b2|
|
||||||
|
if env[:result]
|
||||||
|
b2.use HostMachine
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
b.use Call, IsState, :not_created do |env, b2|
|
b.use Call, IsState, :not_created do |env, b2|
|
||||||
if env[:result]
|
if env[:result]
|
||||||
b2.use Message, I18n.t("docker_provider.messages.not_created")
|
b2.use Message, I18n.t("docker_provider.messages.not_created")
|
||||||
|
@ -98,6 +121,12 @@ module VagrantPlugins
|
||||||
# freeing the resources of the underlying virtual machine.
|
# freeing the resources of the underlying virtual machine.
|
||||||
def self.action_destroy
|
def self.action_destroy
|
||||||
Vagrant::Action::Builder.new.tap do |b|
|
Vagrant::Action::Builder.new.tap do |b|
|
||||||
|
b.use Call, IsState, :host_state_unknown do |env, b2|
|
||||||
|
if env[:result]
|
||||||
|
b2.use HostMachine
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
b.use Call, IsState, :not_created do |env, b2|
|
b.use Call, IsState, :not_created do |env, b2|
|
||||||
if env[:result]
|
if env[:result]
|
||||||
b2.use Message, I18n.t("docker_provider.messages.not_created")
|
b2.use Message, I18n.t("docker_provider.messages.not_created")
|
||||||
|
@ -177,7 +206,12 @@ module VagrantPlugins
|
||||||
Vagrant::Action::Builder.new.tap do |b|
|
Vagrant::Action::Builder.new.tap do |b|
|
||||||
# TODO: b.use SetHostname
|
# TODO: b.use SetHostname
|
||||||
b.use Start
|
b.use Start
|
||||||
b.use WaitForCommunicator
|
|
||||||
|
b.use Call, HasSSH do |env, b2|
|
||||||
|
if env[:result]
|
||||||
|
b2.use WaitForCommunicator
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -186,6 +220,8 @@ module VagrantPlugins
|
||||||
autoload :Create, action_root.join("create")
|
autoload :Create, action_root.join("create")
|
||||||
autoload :Destroy, action_root.join("destroy")
|
autoload :Destroy, action_root.join("destroy")
|
||||||
autoload :ForwardPorts, action_root.join("forward_ports")
|
autoload :ForwardPorts, action_root.join("forward_ports")
|
||||||
|
autoload :HasSSH, action_root.join("has_ssh")
|
||||||
|
autoload :HostMachine, action_root.join("host_machine")
|
||||||
autoload :Stop, action_root.join("stop")
|
autoload :Stop, action_root.join("stop")
|
||||||
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
|
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
|
||||||
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
|
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
|
||||||
|
|
|
@ -16,11 +16,19 @@ module VagrantPlugins
|
||||||
|
|
||||||
guard_cmd_configured!
|
guard_cmd_configured!
|
||||||
|
|
||||||
|
params = create_params
|
||||||
|
|
||||||
cid = ''
|
cid = ''
|
||||||
@@mutex.synchronize do
|
@@mutex.synchronize do
|
||||||
cid = @driver.create(create_params)
|
env[:ui].output(I18n.t("docker_provider.creating"))
|
||||||
|
env[:ui].detail(" Name: #{params[:name]}")
|
||||||
|
env[:ui].detail("Image: #{params[:image]}")
|
||||||
|
|
||||||
|
cid = @driver.create(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
env[:ui].detail(" \n"+I18n.t(
|
||||||
|
"docker_provider.created", id: cid[0...16]))
|
||||||
@machine.id = cid
|
@machine.id = cid
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
end
|
end
|
||||||
|
@ -31,13 +39,14 @@ module VagrantPlugins
|
||||||
container_name << "_#{Time.now.to_i}"
|
container_name << "_#{Time.now.to_i}"
|
||||||
|
|
||||||
{
|
{
|
||||||
image: @provider_config.image,
|
|
||||||
cmd: @provider_config.cmd,
|
cmd: @provider_config.cmd,
|
||||||
ports: forwarded_ports,
|
extra_args: @provider_config.create_args,
|
||||||
name: container_name,
|
|
||||||
hostname: @machine_config.vm.hostname,
|
hostname: @machine_config.vm.hostname,
|
||||||
|
image: @provider_config.image,
|
||||||
|
name: container_name,
|
||||||
|
ports: forwarded_ports,
|
||||||
|
privileged: @provider_config.privileged,
|
||||||
volumes: @provider_config.volumes,
|
volumes: @provider_config.volumes,
|
||||||
privileged: @provider_config.privileged
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")
|
env[:ui].info I18n.t("docker_provider.messages.destroying")
|
||||||
|
|
||||||
machine = env[:machine]
|
machine = env[:machine]
|
||||||
config = machine.provider_config
|
|
||||||
driver = machine.provider.driver
|
driver = machine.provider.driver
|
||||||
|
|
||||||
driver.rm(machine.id)
|
driver.rm(machine.id)
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Action
|
||||||
|
# This middleware is used with Call to test if this machine supports
|
||||||
|
# SSH.
|
||||||
|
class HasSSH
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
env[:result] = env[:machine].provider_config.has_ssh
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,56 @@
|
||||||
|
require "log4r"
|
||||||
|
|
||||||
|
require "vagrant/util/platform"
|
||||||
|
require "vagrant/util/silence_warnings"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Action
|
||||||
|
# This action is responsible for creating the host machine if
|
||||||
|
# we need to. The host machine is where Docker containers will
|
||||||
|
# live.
|
||||||
|
class HostMachine
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::docker::hostmachine")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
if !env[:machine].provider.host_vm?
|
||||||
|
@logger.info("No host machine needed.")
|
||||||
|
return @app.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
env[:machine].ui.output(I18n.t(
|
||||||
|
"docker_provider.host_machine_needed"))
|
||||||
|
|
||||||
|
# TODO(mitchellh): process-level lock so that we don't
|
||||||
|
# step on parallel Vagrant's toes.
|
||||||
|
|
||||||
|
host_machine = env[:machine].provider.host_vm
|
||||||
|
|
||||||
|
# See if the machine is ready already.
|
||||||
|
if host_machine.communicate.ready?
|
||||||
|
env[:machine].ui.detail(I18n.t("docker_provider.host_machine_ready"))
|
||||||
|
return @app.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a UI for this machine that stays at the detail level
|
||||||
|
proxy_ui = host_machine.ui.dup
|
||||||
|
proxy_ui.opts[:bold] = false
|
||||||
|
proxy_ui.opts[:prefix_spaces] = true
|
||||||
|
proxy_ui.opts[:target] = env[:machine].name.to_s
|
||||||
|
|
||||||
|
env[:machine].ui.detail(
|
||||||
|
I18n.t("docker_provider.host_machine_starting"))
|
||||||
|
env[:machine].ui.detail(" ")
|
||||||
|
host_machine.with_ui(proxy_ui) do
|
||||||
|
host_machine.action(:up)
|
||||||
|
end
|
||||||
|
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,25 +3,66 @@ module VagrantPlugins
|
||||||
class Config < Vagrant.plugin("2", :config)
|
class Config < Vagrant.plugin("2", :config)
|
||||||
attr_accessor :image, :cmd, :ports, :volumes, :privileged
|
attr_accessor :image, :cmd, :ports, :volumes, :privileged
|
||||||
|
|
||||||
|
# Additional arguments to pass to `docker run` when creating
|
||||||
|
# the container for the first time. This is an array of args.
|
||||||
|
#
|
||||||
|
# @return [Array<String>]
|
||||||
|
attr_accessor :create_args
|
||||||
|
|
||||||
|
# True if the Docker container exposes SSH access. If this is true,
|
||||||
|
# then Vagrant can do a bunch more things like setting the hostname,
|
||||||
|
# provisioning, etc.
|
||||||
|
attr_accessor :has_ssh
|
||||||
|
|
||||||
|
# The name of the machine in the Vagrantfile set with
|
||||||
|
# "vagrant_vagrantfile" that will be the docker host. Defaults
|
||||||
|
# to "default"
|
||||||
|
#
|
||||||
|
# See the "vagrant_vagrantfile" docs for more info.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :vagrant_machine
|
||||||
|
|
||||||
|
# The path to the Vagrantfile that contains a VM that will be
|
||||||
|
# started as the Docker host if needed (Windows, OS X, Linux
|
||||||
|
# without container support).
|
||||||
|
#
|
||||||
|
# Defaults to a built-in Vagrantfile that will load boot2docker.
|
||||||
|
#
|
||||||
|
# NOTE: This only has an effect if Vagrant needs a Docker host.
|
||||||
|
# Vagrant determines this automatically based on the environment
|
||||||
|
# it is running in.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :vagrant_vagrantfile
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@cmd = UNSET_VALUE
|
@cmd = UNSET_VALUE
|
||||||
|
@create_args = []
|
||||||
|
@has_ssh = UNSET_VALUE
|
||||||
@image = UNSET_VALUE
|
@image = UNSET_VALUE
|
||||||
@ports = []
|
@ports = []
|
||||||
@privileged = UNSET_VALUE
|
@privileged = UNSET_VALUE
|
||||||
@volumes = []
|
@volumes = []
|
||||||
|
@vagrant_machine = UNSET_VALUE
|
||||||
|
@vagrant_vagrantfile = UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize!
|
def finalize!
|
||||||
@cmd = [] if @cmd == UNSET_VALUE
|
@cmd = [] if @cmd == UNSET_VALUE
|
||||||
|
@create_args = [] if @create_args == UNSET_VALUE
|
||||||
|
@has_ssh = false if @has_ssh == UNSET_VALUE
|
||||||
@image = nil if @image == UNSET_VALUE
|
@image = nil if @image == UNSET_VALUE
|
||||||
@privileged = false if @privileged == UNSET_VALUE
|
@privileged = false if @privileged == UNSET_VALUE
|
||||||
|
@vagrant_machine = nil if @vagrant_machine == UNSET_VALUE
|
||||||
|
@vagrant_vagrantfile = nil if @vagrant_vagrantfile == UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(machine)
|
def validate(machine)
|
||||||
errors = _detected_errors
|
errors = _detected_errors
|
||||||
|
|
||||||
# TODO: Detect if base image has a CMD / ENTRYPOINT set before erroring out
|
# TODO: Detect if base image has a CMD / ENTRYPOINT set before erroring out
|
||||||
errors << I18n.t("docker_provider.errors.config.cmd_not_set") if @cmd == UNSET_VALUE
|
errors << I18n.t("docker_provider.errors.config.cmd_not_set") if @cmd == UNSET_VALUE
|
||||||
|
|
||||||
{ "docker provider" => errors }
|
{ "docker provider" => errors }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
require "vagrant/util/busy"
|
require "json"
|
||||||
require "vagrant/util/subprocess"
|
|
||||||
require "vagrant/util/retryable"
|
|
||||||
|
|
||||||
require 'log4r'
|
require "log4r"
|
||||||
require 'json'
|
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module DockerProvider
|
module DockerProvider
|
||||||
class Driver
|
class Driver
|
||||||
include Vagrant::Util::Retryable
|
# The executor is responsible for actually executing Docker commands.
|
||||||
|
# This is set by the provider, but defaults to local execution.
|
||||||
|
attr_accessor :executor
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@logger = Log4r::Logger.new("vagrant::docker::driver")
|
@logger = Log4r::Logger.new("vagrant::docker::driver")
|
||||||
|
@executor = Executor::Local.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(params)
|
def create(params)
|
||||||
|
@ -26,6 +26,7 @@ module VagrantPlugins
|
||||||
run_cmd += volumes.map { |v| ['-v', v.to_s] }
|
run_cmd += volumes.map { |v| ['-v', v.to_s] }
|
||||||
run_cmd += %W(--privileged) if params[:privileged]
|
run_cmd += %W(--privileged) if params[:privileged]
|
||||||
run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
|
run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
|
||||||
|
run_cmd += params[:extra_args] if params[:extra_args]
|
||||||
run_cmd += [image, cmd]
|
run_cmd += [image, cmd]
|
||||||
|
|
||||||
execute(*run_cmd.flatten).chomp
|
execute(*run_cmd.flatten).chomp
|
||||||
|
@ -101,35 +102,7 @@ module VagrantPlugins
|
||||||
private
|
private
|
||||||
|
|
||||||
def execute(*cmd, &block)
|
def execute(*cmd, &block)
|
||||||
result = raw(*cmd, &block)
|
@executor.execute(*cmd, &block)
|
||||||
|
|
||||||
if result.exit_code != 0
|
|
||||||
if @interrupted
|
|
||||||
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
|
||||||
else
|
|
||||||
msg = result.stdout.gsub("\r\n", "\n")
|
|
||||||
msg << result.stderr.gsub("\r\n", "\n")
|
|
||||||
raise "#{cmd.inspect}\n#{msg}" #Errors::ExecuteError, :command => command.inspect
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the output, making sure to replace any Windows-style
|
|
||||||
# newlines with Unix-style.
|
|
||||||
result.stdout.gsub("\r\n", "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
def raw(*cmd, &block)
|
|
||||||
int_callback = lambda do
|
|
||||||
@interrupted = true
|
|
||||||
@logger.info("Interrupted.")
|
|
||||||
end
|
|
||||||
|
|
||||||
# Append in the options for subprocess
|
|
||||||
cmd << { :notify => [:stdout, :stderr] }
|
|
||||||
|
|
||||||
Vagrant::Util::Busy.busy(int_callback) do
|
|
||||||
Vagrant::Util::Subprocess.execute(*cmd, &block)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,10 @@ module VagrantPlugins
|
||||||
error_namespace("docker_provider.errors")
|
error_namespace("docker_provider.errors")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ExecuteError < DockerError
|
||||||
|
error_key(:execute_error)
|
||||||
|
end
|
||||||
|
|
||||||
class ImageNotConfiguredError < DockerError
|
class ImageNotConfiguredError < DockerError
|
||||||
error_key(:docker_provider_image_not_configured)
|
error_key(:docker_provider_image_not_configured)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
require "vagrant/util/busy"
|
||||||
|
require "vagrant/util/subprocess"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Executor
|
||||||
|
# The Local executor executes a Docker client that is running
|
||||||
|
# locally.
|
||||||
|
class Local
|
||||||
|
def execute(*cmd, &block)
|
||||||
|
# Append in the options for subprocess
|
||||||
|
cmd << { :notify => [:stdout, :stderr] }
|
||||||
|
|
||||||
|
interrupted = false
|
||||||
|
int_callback = ->{ interrupted = true }
|
||||||
|
result = Vagrant::Util::Busy.busy(int_callback) do
|
||||||
|
Vagrant::Util::Subprocess.execute(*cmd, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
if result.exit_code != 0 && !interrupted
|
||||||
|
msg = result.stdout.gsub("\r\n", "\n")
|
||||||
|
msg << result.stderr.gsub("\r\n", "\n")
|
||||||
|
raise "#{cmd.inspect}\n#{msg}" #Errors::ExecuteError, :command => command.inspect
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return the output, making sure to replace any Windows-style
|
||||||
|
# newlines with Unix-style.
|
||||||
|
result.stdout.gsub("\r\n", "\n")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,58 @@
|
||||||
|
require "vagrant/util/shell_quote"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Executor
|
||||||
|
# The Vagrant executor runs Docker over SSH against the given
|
||||||
|
# Vagrant-managed machine.
|
||||||
|
class Vagrant
|
||||||
|
def initialize(host_machine)
|
||||||
|
@host_machine = host_machine
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(*cmd, &block)
|
||||||
|
quote = '"'
|
||||||
|
cmd = cmd.map do |a|
|
||||||
|
"#{quote}#{::Vagrant::Util::ShellQuote.escape(a, quote)}#{quote}"
|
||||||
|
end.join(" ")
|
||||||
|
|
||||||
|
# Add a start fence so we know when to start reading output.
|
||||||
|
# We have to do this because boot2docker outputs a login shell
|
||||||
|
# boot2docker version that we get otherwise and messes up output.
|
||||||
|
start_fence = "========== VAGRANT DOCKER BEGIN =========="
|
||||||
|
ssh_cmd = "echo \"#{start_fence}\"; #{cmd}"
|
||||||
|
|
||||||
|
stderr = ""
|
||||||
|
stdout = ""
|
||||||
|
fenced = false
|
||||||
|
comm = @host_machine.communicate
|
||||||
|
code = comm.execute(ssh_cmd, error_check: false) do |type, data|
|
||||||
|
next if ![:stdout, :stderr].include?(type)
|
||||||
|
stderr << data if type == :stderr
|
||||||
|
stdout << data if type == :stdout
|
||||||
|
|
||||||
|
if !fenced
|
||||||
|
index = stdout.index(start_fence)
|
||||||
|
if index
|
||||||
|
index += start_fence.length
|
||||||
|
stdout = stdout[index..-1]
|
||||||
|
stdout.chomp!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
block.call(type, data) if block && fenced
|
||||||
|
end
|
||||||
|
|
||||||
|
if code != 0
|
||||||
|
raise Errors::ExecuteError,
|
||||||
|
command: cmd,
|
||||||
|
stderr: stderr.chomp,
|
||||||
|
stdout: stdout.chomp
|
||||||
|
end
|
||||||
|
|
||||||
|
stdout.chomp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.box = "mitchellh/boot2docker"
|
||||||
|
|
||||||
|
config.vm.provider "virtualbox" do |v|
|
||||||
|
v.check_guest_additions = false
|
||||||
|
v.functional_vboxsf = false
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,11 @@ module VagrantPlugins
|
||||||
autoload :Driver, File.expand_path("../driver", __FILE__)
|
autoload :Driver, File.expand_path("../driver", __FILE__)
|
||||||
autoload :Errors, File.expand_path("../errors", __FILE__)
|
autoload :Errors, File.expand_path("../errors", __FILE__)
|
||||||
|
|
||||||
|
module Executor
|
||||||
|
autoload :Local, File.expand_path("../executor/local", __FILE__)
|
||||||
|
autoload :Vagrant, File.expand_path("../executor/vagrant", __FILE__)
|
||||||
|
end
|
||||||
|
|
||||||
class Plugin < Vagrant.plugin("2")
|
class Plugin < Vagrant.plugin("2")
|
||||||
name "docker-provider"
|
name "docker-provider"
|
||||||
description <<-EOF
|
description <<-EOF
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
|
require "fileutils"
|
||||||
|
|
||||||
require "log4r"
|
require "log4r"
|
||||||
|
|
||||||
|
require "vagrant/action/builtin/mixin_synced_folders"
|
||||||
|
require "vagrant/util/silence_warnings"
|
||||||
|
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module DockerProvider
|
module DockerProvider
|
||||||
class Provider < Vagrant.plugin("2", :provider)
|
class Provider < Vagrant.plugin("2", :provider)
|
||||||
attr_reader :driver
|
|
||||||
|
|
||||||
def initialize(machine)
|
def initialize(machine)
|
||||||
@logger = Log4r::Logger.new("vagrant::provider::docker")
|
@logger = Log4r::Logger.new("vagrant::provider::docker")
|
||||||
@machine = machine
|
@machine = machine
|
||||||
@driver = Driver.new
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# @see Vagrant::Plugin::V2::Provider#action
|
# @see Vagrant::Plugin::V2::Provider#action
|
||||||
|
@ -18,13 +20,107 @@ module VagrantPlugins
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# 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 host_vm?
|
||||||
|
@driver.executor = Executor::Vagrant.new(host_vm)
|
||||||
|
end
|
||||||
|
|
||||||
|
@driver
|
||||||
|
end
|
||||||
|
|
||||||
|
# This returns the {Vagrant::Machine} that is our host machine.
|
||||||
|
# It does not perform any action on the machine or verify it is
|
||||||
|
# running.
|
||||||
|
#
|
||||||
|
# @return [Vagrant::Machine]
|
||||||
|
def host_vm
|
||||||
|
return @host_vm if @host_vm
|
||||||
|
|
||||||
|
# TODO(mitchellh): process-wide lock
|
||||||
|
|
||||||
|
vf_path = @machine.provider_config.vagrant_vagrantfile
|
||||||
|
host_machine_name = @machine.provider_config.vagrant_machine || :default
|
||||||
|
if !vf_path
|
||||||
|
# We don't have a Vagrantfile path set, so we're going to use
|
||||||
|
# the default but we need to copy it into the data dir so that
|
||||||
|
# we don't write into our installation dir (we can't).
|
||||||
|
default_path = File.expand_path("../hostmachine/Vagrantfile", __FILE__)
|
||||||
|
vf_path = @machine.env.data_dir.join("docker-host", "Vagrantfile")
|
||||||
|
vf_path.dirname.mkpath
|
||||||
|
FileUtils.cp(default_path, vf_path)
|
||||||
|
|
||||||
|
# Set the machine name since we hardcode that for the default
|
||||||
|
host_machine_name = :default
|
||||||
|
end
|
||||||
|
|
||||||
|
vf_file = File.basename(vf_path)
|
||||||
|
vf_path = File.dirname(vf_path)
|
||||||
|
|
||||||
|
# Create the env to manage this machine
|
||||||
|
@host_vm = Vagrant::Util::SilenceWarnings.silence! do
|
||||||
|
host_env = Vagrant::Environment.new(
|
||||||
|
cwd: vf_path,
|
||||||
|
home_path: @machine.env.home_path,
|
||||||
|
ui_class: @machine.env.ui_class,
|
||||||
|
vagrantfile_name: vf_file,
|
||||||
|
)
|
||||||
|
|
||||||
|
# TODO(mitchellh): configure the provider of this machine somehow
|
||||||
|
host_env.machine(host_machine_name, :virtualbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure we swap all the synced folders out from our
|
||||||
|
# machine so that we do a double synced folder: normal synced
|
||||||
|
# folders to the host machine, then Docker volumes within that host.
|
||||||
|
sf_helper_klass = Class.new do
|
||||||
|
include Vagrant::Action::Builtin::MixinSyncedFolders
|
||||||
|
end
|
||||||
|
sf_helper = sf_helper_klass.new
|
||||||
|
our_folders = sf_helper.synced_folders(@machine)
|
||||||
|
if our_folders[:docker]
|
||||||
|
our_folders[:docker].each do |id, data|
|
||||||
|
data = data.dup
|
||||||
|
data.delete(:type)
|
||||||
|
|
||||||
|
# Add them to the host machine
|
||||||
|
=begin
|
||||||
|
@host_vm.config.vm.synced_folder(
|
||||||
|
data[:hostpath],
|
||||||
|
data[:guestpath],
|
||||||
|
data)
|
||||||
|
=end
|
||||||
|
|
||||||
|
# Remove from our machine
|
||||||
|
@machine.config.vm.synced_folders.delete(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@host_vm
|
||||||
|
end
|
||||||
|
|
||||||
|
# This says whether or not Docker will be running within a VM
|
||||||
|
# rather than directly on our system. Docker needs to run in a VM
|
||||||
|
# when we're not on Linux, or not on a Linux that supports Docker.
|
||||||
|
def host_vm?
|
||||||
|
# TODO: It'd be nice to also check if Docker supports the version
|
||||||
|
# of Linux that Vagrant is running on so that we can spin up a VM
|
||||||
|
# on old versions of Linux as well.
|
||||||
|
!Vagrant::Util::Platform.linux?
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the SSH info for accessing the Container.
|
# Returns the SSH info for accessing the Container.
|
||||||
def ssh_info
|
def ssh_info
|
||||||
# If the Container is not created then we cannot possibly SSH into it, so
|
# If the Container is not created then we cannot possibly SSH into it, so
|
||||||
# we return nil.
|
# we return nil.
|
||||||
return nil if state == :not_created
|
return nil if state == :not_created
|
||||||
|
|
||||||
network = @driver.inspect_container(@machine.id)['NetworkSettings']
|
network = driver.inspect_container(@machine.id)['NetworkSettings']
|
||||||
ip = network['IPAddress']
|
ip = network['IPAddress']
|
||||||
|
|
||||||
# If we were not able to identify the container's IP, we return nil
|
# If we were not able to identify the container's IP, we return nil
|
||||||
|
@ -39,12 +135,14 @@ module VagrantPlugins
|
||||||
|
|
||||||
def state
|
def state
|
||||||
state_id = nil
|
state_id = nil
|
||||||
state_id = :not_created if !@machine.id || !@driver.created?(@machine.id)
|
state_id = :host_state_unknown if host_vm? && !host_vm.communicate.ready?
|
||||||
state_id = @driver.state(@machine.id) if @machine.id && !state_id
|
state_id = :not_created if !state_id && \
|
||||||
|
(!@machine.id || !driver.created?(@machine.id))
|
||||||
|
state_id = driver.state(@machine.id) if @machine.id && !state_id
|
||||||
state_id = :unknown if !state_id
|
state_id = :unknown if !state_id
|
||||||
|
|
||||||
short = state_id.to_s.gsub("_", " ")
|
short = state_id.to_s.gsub("_", " ")
|
||||||
long = I18n.t("vagrant.commands.status.#{state_id}")
|
long = I18n.t("docker_provider.status.#{state_id}")
|
||||||
|
|
||||||
Vagrant::MachineState.new(state_id, short, long)
|
Vagrant::MachineState.new(state_id, short, long)
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,6 +38,13 @@ module VagrantPlugins
|
||||||
# @return [String]
|
# @return [String]
|
||||||
attr_accessor :name
|
attr_accessor :name
|
||||||
|
|
||||||
|
# Whether or not this VM has a functional vboxsf filesystem module.
|
||||||
|
# This defaults to true. If you set this to false, then the "virtualbox"
|
||||||
|
# synced folder type won't be valid.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
attr_accessor :functional_vboxsf
|
||||||
|
|
||||||
# The defined network adapters.
|
# The defined network adapters.
|
||||||
#
|
#
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
|
@ -48,6 +55,7 @@ module VagrantPlugins
|
||||||
@check_guest_additions = UNSET_VALUE
|
@check_guest_additions = UNSET_VALUE
|
||||||
@customizations = []
|
@customizations = []
|
||||||
@destroy_unused_network_interfaces = UNSET_VALUE
|
@destroy_unused_network_interfaces = UNSET_VALUE
|
||||||
|
@functional_vboxsf = UNSET_VALUE
|
||||||
@name = UNSET_VALUE
|
@name = UNSET_VALUE
|
||||||
@network_adapters = {}
|
@network_adapters = {}
|
||||||
@gui = UNSET_VALUE
|
@gui = UNSET_VALUE
|
||||||
|
@ -113,6 +121,10 @@ module VagrantPlugins
|
||||||
@destroy_unused_network_interfaces = false
|
@destroy_unused_network_interfaces = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if @functional_vboxsf == UNSET_VALUE
|
||||||
|
@functional_vboxsf = true
|
||||||
|
end
|
||||||
|
|
||||||
# Default is to not show a GUI
|
# Default is to not show a GUI
|
||||||
@gui = false if @gui == UNSET_VALUE
|
@gui = false if @gui == UNSET_VALUE
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ require "vagrant/util/platform"
|
||||||
module VagrantPlugins
|
module VagrantPlugins
|
||||||
module ProviderVirtualBox
|
module ProviderVirtualBox
|
||||||
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
||||||
def usable?(machine)
|
def usable?(machine, raise_errors=false)
|
||||||
# These synced folders only work if the provider if VirtualBox
|
# These synced folders only work if the provider if VirtualBox
|
||||||
machine.provider_name == :virtualbox
|
machine.provider_name == :virtualbox &&
|
||||||
|
machine.provider_config.functional_vboxsf
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare(machine, folders, _opts)
|
def prepare(machine, folders, _opts)
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
en:
|
en:
|
||||||
docker_provider:
|
docker_provider:
|
||||||
|
creating: |-
|
||||||
|
Creating the container...
|
||||||
|
created: |-
|
||||||
|
Container created: %{id}
|
||||||
|
host_machine_needed: |-
|
||||||
|
Docker host is required. One will be created if necessary...
|
||||||
|
host_machine_ready: |-
|
||||||
|
Docker host VM is already ready.
|
||||||
|
host_machine_starting: |-
|
||||||
|
Vagrant will now create or start a local VM to act as the Docker
|
||||||
|
host. You'll see the output of the `vagrant up` for this VM below.
|
||||||
|
|
||||||
messages:
|
messages:
|
||||||
|
destroying: |-
|
||||||
|
Deleting the container...
|
||||||
not_created: |-
|
not_created: |-
|
||||||
The container hasn't been created yet.
|
The container hasn't been created yet.
|
||||||
not_running: |-
|
not_running: |-
|
||||||
The container is not currently running.
|
The container is not currently running.
|
||||||
|
provision_no_ssh: |-
|
||||||
|
Provisioners will not be run since container doesn't support SSH.
|
||||||
will_not_destroy: |-
|
will_not_destroy: |-
|
||||||
The container will not be destroyed, since the confirmation was declined.
|
The container will not be destroyed, since the confirmation was declined.
|
||||||
starting: |-
|
starting: |-
|
||||||
|
@ -14,6 +30,23 @@ en:
|
||||||
container_ready: |-
|
container_ready: |-
|
||||||
Container started and ready for use!
|
Container started and ready for use!
|
||||||
|
|
||||||
|
status:
|
||||||
|
host_state_unknown: |-
|
||||||
|
The host VM for the Docker containers appears to not be running
|
||||||
|
or is currently inaccessible. Because of this, we can't determine
|
||||||
|
the state of the containers on that host. Run `vagrant up` to
|
||||||
|
bring up the host VM again.
|
||||||
|
not_created: |-
|
||||||
|
The environment has not yet been created. Run `vagrant up` to
|
||||||
|
create the environment. If a machine is not created, only the
|
||||||
|
default provider will be shown. So if a provider is not listed,
|
||||||
|
then the machine is not created for that environment.
|
||||||
|
stopped: |-
|
||||||
|
The container is created but not running. You can run it again
|
||||||
|
with `vagrant up`. If the container always goes to "stopped"
|
||||||
|
right away after being started, it is because the command being
|
||||||
|
run exits and doesn't keep running.
|
||||||
|
|
||||||
errors:
|
errors:
|
||||||
config:
|
config:
|
||||||
cmd_not_set: |-
|
cmd_not_set: |-
|
||||||
|
@ -25,6 +58,16 @@ en:
|
||||||
and try again.
|
and try again.
|
||||||
docker_provider_image_not_configured: |-
|
docker_provider_image_not_configured: |-
|
||||||
The base Docker image has not been set for the '%{name}' VM!
|
The base Docker image has not been set for the '%{name}' VM!
|
||||||
|
execute_error: |-
|
||||||
|
A Docker command executed by Vagrant didn't complete successfully!
|
||||||
|
The command run along with the output from the command is shown
|
||||||
|
below.
|
||||||
|
|
||||||
|
Command: %{command}
|
||||||
|
|
||||||
|
Stderr: %{stderr}
|
||||||
|
|
||||||
|
Stdout: %{stdout}
|
||||||
synced_folder_non_docker: |-
|
synced_folder_non_docker: |-
|
||||||
The "docker" synced folder type can't be used because the provider
|
The "docker" synced folder type can't be used because the provider
|
||||||
in use is not Docker. This synced folder type only works with the
|
in use is not Docker. This synced folder type only works with the
|
||||||
|
|
|
@ -1,6 +1,43 @@
|
||||||
require_relative "../../../base"
|
require_relative "../../../base"
|
||||||
|
|
||||||
|
require "vagrant/util/platform"
|
||||||
|
|
||||||
require Vagrant.source_root.join("plugins/providers/docker/config")
|
require Vagrant.source_root.join("plugins/providers/docker/config")
|
||||||
|
|
||||||
describe VagrantPlugins::DockerProvider::Config do
|
describe VagrantPlugins::DockerProvider::Config do
|
||||||
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
|
def assert_invalid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.values.any? { |v| !v.empty? }
|
||||||
|
raise "No errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_valid
|
||||||
|
errors = subject.validate(machine)
|
||||||
|
if !errors.values.all? { |v| v.empty? }
|
||||||
|
raise "Errors: #{errors.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "defaults" do
|
||||||
|
before { subject.finalize! }
|
||||||
|
|
||||||
|
its(:cmd) { should eq([]) }
|
||||||
|
its(:image) { should be_nil }
|
||||||
|
its(:privileged) { should be_false }
|
||||||
|
its(:vagrant_machine) { should be_nil }
|
||||||
|
its(:vagrant_vagrantfile) { should be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
# By default lets be Linux for validations
|
||||||
|
Vagrant::Util::Platform.stub(linux: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be valid by default" do
|
||||||
|
subject.finalize!
|
||||||
|
assert_valid
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,6 +11,7 @@ describe VagrantPlugins::ProviderVirtualBox::Config do
|
||||||
it { expect(subject.check_guest_additions).to be_true }
|
it { expect(subject.check_guest_additions).to be_true }
|
||||||
it { expect(subject.gui).to be_false }
|
it { expect(subject.gui).to be_false }
|
||||||
it { expect(subject.name).to be_nil }
|
it { expect(subject.name).to be_nil }
|
||||||
|
it { expect(subject.functional_vboxsf).to be_true }
|
||||||
|
|
||||||
it "should have one NAT adapter" do
|
it "should have one NAT adapter" do
|
||||||
expect(subject.network_adapters).to eql({
|
expect(subject.network_adapters).to eql({
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
require "vagrant"
|
require "vagrant"
|
||||||
require Vagrant.source_root.join("test/unit/base")
|
require Vagrant.source_root.join("test/unit/base")
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/providers/virtualbox/config")
|
||||||
require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder")
|
require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder")
|
||||||
|
|
||||||
describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
|
describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
|
||||||
let(:machine) do
|
let(:machine) do
|
||||||
double("machine").tap do |m|
|
double("machine").tap do |m|
|
||||||
|
m.stub(provider_config: VagrantPlugins::ProviderVirtualBox::Config.new)
|
||||||
|
m.stub(provider_name: :virtualbox)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { described_class.new }
|
subject { described_class.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.provider_config.finalize!
|
||||||
|
end
|
||||||
|
|
||||||
describe "usable" do
|
describe "usable" do
|
||||||
it "should be with virtualbox provider" do
|
it "should be with virtualbox provider" do
|
||||||
machine.stub(provider_name: :virtualbox)
|
machine.stub(provider_name: :virtualbox)
|
||||||
|
@ -21,6 +28,11 @@ describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do
|
||||||
machine.stub(provider_name: :vmware_fusion)
|
machine.stub(provider_name: :vmware_fusion)
|
||||||
expect(subject).not_to be_usable(machine)
|
expect(subject).not_to be_usable(machine)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should not be usable if not functional vboxsf" do
|
||||||
|
machine.provider_config.functional_vboxsf = false
|
||||||
|
expect(subject).to_not be_usable(machine)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare" do
|
describe "prepare" do
|
||||||
|
|
|
@ -16,6 +16,20 @@ describe Vagrant::Action::Builder do
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def wrapper_proc(data)
|
||||||
|
Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:call) do |env|
|
||||||
|
env[:data] << "#{data}_in"
|
||||||
|
@app.call(env)
|
||||||
|
env[:data] << "#{data}_out"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "copying" do
|
context "copying" do
|
||||||
it "should copy the stack" do
|
it "should copy the stack" do
|
||||||
copy = subject.dup
|
copy = subject.dup
|
||||||
|
@ -239,4 +253,34 @@ describe Vagrant::Action::Builder do
|
||||||
subject.call(data)
|
subject.call(data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "calling another app later" do
|
||||||
|
it "calls in the proper order" do
|
||||||
|
# We have to do this because inside the Class.new, it can't see these
|
||||||
|
# rspec methods...
|
||||||
|
described_klass = described_class
|
||||||
|
wrapper_proc = self.method(:wrapper_proc)
|
||||||
|
|
||||||
|
wrapper = Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:call) do |env|
|
||||||
|
inner = described_klass.new
|
||||||
|
inner.use wrapper_proc[2]
|
||||||
|
inner.use @app
|
||||||
|
inner.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.use wrapper_proc(1)
|
||||||
|
subject.use wrapper
|
||||||
|
subject.use wrapper_proc(3)
|
||||||
|
subject.call(data)
|
||||||
|
|
||||||
|
expect(data[:data]).to eq([
|
||||||
|
"1_in", "2_in", "3_in", "3_out", "2_out", "1_out"])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,20 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
let(:app) { lambda { |env| } }
|
let(:app) { lambda { |env| } }
|
||||||
let(:env) { {} }
|
let(:env) { {} }
|
||||||
|
|
||||||
|
def wrapper_proc(data)
|
||||||
|
Class.new do
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
end
|
||||||
|
|
||||||
|
define_method(:call) do |env|
|
||||||
|
env[:data] << "#{data}_in"
|
||||||
|
@app.call(env)
|
||||||
|
env[:data] << "#{data}_out"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it "should yield the env to the block" do
|
it "should yield the env to the block" do
|
||||||
received = nil
|
received = nil
|
||||||
|
|
||||||
|
@ -64,6 +78,23 @@ describe Vagrant::Action::Builtin::Call do
|
||||||
expect(received).to eq(:bar)
|
expect(received).to eq(:bar)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "should call the next builder inserted in our own stack" do
|
||||||
|
callable = lambda { |env| }
|
||||||
|
|
||||||
|
builder = Vagrant::Action::Builder.new.tap do |b|
|
||||||
|
b.use wrapper_proc(1)
|
||||||
|
b.use described_class, callable do |_env, b2|
|
||||||
|
b2.use wrapper_proc(2)
|
||||||
|
end
|
||||||
|
b.use wrapper_proc(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
env = { data: [] }
|
||||||
|
builder.call(env)
|
||||||
|
expect(env[:data]).to eq([
|
||||||
|
"1_in", "2_in", "3_in", "3_out", "2_out", "1_out"])
|
||||||
|
end
|
||||||
|
|
||||||
it "should instantiate the callable with the extra args" do
|
it "should instantiate the callable with the extra args" do
|
||||||
env = {}
|
env = {}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,17 @@ describe Vagrant::Action::Builtin::Message do
|
||||||
it "outputs the given message" do
|
it "outputs the given message" do
|
||||||
subject = described_class.new(app, env, "foo")
|
subject = described_class.new(app, env, "foo")
|
||||||
|
|
||||||
expect(ui).to receive(:output).with("foo")
|
expect(ui).to receive(:output).with("foo").ordered
|
||||||
expect(app).to receive(:call).with(env)
|
expect(app).to receive(:call).with(env).ordered
|
||||||
|
|
||||||
|
subject.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "outputs the given message after the call" do
|
||||||
|
subject = described_class.new(app, env, "foo", post: true)
|
||||||
|
|
||||||
|
expect(app).to receive(:call).with(env).ordered
|
||||||
|
expect(ui).to receive(:output).with("foo").ordered
|
||||||
|
|
||||||
subject.call(env)
|
subject.call(env)
|
||||||
end
|
end
|
||||||
|
|
|
@ -341,5 +341,10 @@ describe Vagrant::UI::Prefixed do
|
||||||
expect(ui).to receive(:output).with("==> #{prefix}: foo", {})
|
expect(ui).to receive(:output).with("==> #{prefix}: foo", {})
|
||||||
subject.output("foo")
|
subject.output("foo")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "prefixes with another prefix if requested" do
|
||||||
|
expect(ui).to receive(:output).with("==> bar: foo", anything)
|
||||||
|
subject.output("foo", target: "bar")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue