mirror of
https://github.com/hashicorp/vagrant.git
synced 2026-06-03 22:05:01 -04:00
Remove customized require behaviors and modify the bin executable to check for missing tools that Vagrant expects to exist when running outside of an installer.
215 lines
6.7 KiB
Ruby
215 lines
6.7 KiB
Ruby
# Copyright (c) HashiCorp, Inc.
|
|
# SPDX-License-Identifier: BUSL-1.1
|
|
|
|
require "digest/md5"
|
|
require "fileutils"
|
|
require "thread"
|
|
require "log4r"
|
|
|
|
require "vagrant/util/silence_warnings"
|
|
|
|
module VagrantPlugins
|
|
module DockerProvider
|
|
class Provider < Vagrant.plugin("2", :provider)
|
|
@@host_vm_mutex = Mutex.new
|
|
|
|
def self.usable?(raise_error=false)
|
|
Driver.new.execute("docker", "version")
|
|
true
|
|
rescue Vagrant::Errors::CommandUnavailable, Errors::ExecuteError
|
|
raise if raise_error
|
|
return false
|
|
end
|
|
|
|
def initialize(machine)
|
|
@logger = Log4r::Logger.new("vagrant::provider::docker")
|
|
@machine = machine
|
|
|
|
if host_vm?
|
|
# We need to use a special communicator that proxies our
|
|
# SSH requests over our host VM to the container itself.
|
|
@machine.config.vm.communicator = :docker_hostvm
|
|
end
|
|
end
|
|
|
|
# @see Vagrant::Plugin::V2::Provider#action
|
|
def action(name)
|
|
action_method = "action_#{name}"
|
|
return Action.send(action_method) if Action.respond_to?(action_method)
|
|
nil
|
|
end
|
|
|
|
# Returns the driver instance for this provider.
|
|
def driver
|
|
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
|
|
|
|
@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
|
|
|
|
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")
|
|
begin
|
|
@machine.env.lock("docker-provider-hostvm") do
|
|
vf_path.dirname.mkpath
|
|
FileUtils.cp(default_path, vf_path)
|
|
end
|
|
rescue Vagrant::Errors::EnvironmentLockedError
|
|
# Lock contention, just retry
|
|
retry
|
|
end
|
|
|
|
# Set the machine name since we hardcode that for the default
|
|
host_machine_name = :default
|
|
end
|
|
|
|
# Expand it so that the home directories and so on get processed
|
|
# properly.
|
|
vf_path = File.expand_path(vf_path, @machine.env.root_path)
|
|
|
|
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,
|
|
)
|
|
|
|
# If there is no root path, then the Vagrantfile wasn't found
|
|
# and it is an error...
|
|
raise Errors::VagrantfileNotFound if !host_env.root_path
|
|
|
|
host_env.machine(
|
|
host_machine_name,
|
|
host_env.default_provider(
|
|
exclude: [:docker],
|
|
force_default: false,
|
|
))
|
|
end
|
|
|
|
@host_vm
|
|
end
|
|
|
|
# This acquires a lock on the host VM.
|
|
def host_vm_lock
|
|
hash = Digest::MD5.hexdigest(host_vm.data_dir.to_s)
|
|
|
|
# We do a process-level mutex on the outside, since we can
|
|
# wait for that a short amount of time. Then, we do a process lock
|
|
# on the inside, which will raise an exception if locked.
|
|
host_vm_mutex.synchronize do
|
|
@machine.env.lock(hash) do
|
|
return yield
|
|
end
|
|
end
|
|
end
|
|
|
|
# This is a process-local mutex that can be used by parallel
|
|
# providers to lock the host VM access.
|
|
def host_vm_mutex
|
|
@@host_vm_mutex
|
|
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?
|
|
@machine.provider_config.force_host_vm
|
|
end
|
|
|
|
# Returns the SSH info for accessing the Container.
|
|
def ssh_info
|
|
# If the container isn't running, we can't SSH into it
|
|
return nil if state.id != :running
|
|
|
|
port_name = "#{@machine.config.ssh.guest_port}/tcp"
|
|
network = driver.inspect_container(@machine.id)['NetworkSettings']
|
|
|
|
if network["Ports"][port_name].respond_to?(:first)
|
|
port_info = network["Ports"][port_name].first
|
|
else
|
|
ip = network["IPAddress"]
|
|
port = @machine.config.ssh.guest_port
|
|
if !ip.to_s.empty?
|
|
port_info = {
|
|
"HostIp" => ip,
|
|
"HostPort" => port
|
|
}
|
|
end
|
|
end
|
|
|
|
# If we were not able to identify the container's IP, we return nil
|
|
# here and we let Vagrant core deal with it ;)
|
|
return nil if port_info.nil? || port_info.empty?
|
|
|
|
{
|
|
host: port_info['HostIp'],
|
|
port: port_info['HostPort']
|
|
}
|
|
end
|
|
|
|
def state
|
|
state_id = nil
|
|
state_id = :not_created if !@machine.id
|
|
|
|
begin
|
|
state_id = :host_state_unknown if !state_id && \
|
|
host_vm? && !host_vm.communicate.ready?
|
|
rescue Errors::VagrantfileNotFound
|
|
state_id = :host_state_unknown
|
|
end
|
|
|
|
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
|
|
|
|
# This is a special pseudo-state so that we don't set the
|
|
# NOT_CREATED_ID while we're setting up the machine. This avoids
|
|
# clearing the data dir.
|
|
state_id = :preparing if @machine.id == "preparing"
|
|
|
|
short = state_id.to_s.gsub("_", " ")
|
|
long = I18n.t("docker_provider.status.#{state_id}")
|
|
|
|
# If we're not created, then specify the special ID flag
|
|
if state_id == :not_created
|
|
state_id = Vagrant::MachineState::NOT_CREATED_ID
|
|
end
|
|
|
|
Vagrant::MachineState.new(state_id, short, long)
|
|
end
|
|
|
|
def to_s
|
|
id = @machine.id ? @machine.id : "new container"
|
|
"Docker (#{id})"
|
|
end
|
|
end
|
|
end
|
|
end
|