dc55c914e4
The LXC provider issues the "fetch_ip" action to look up the IP address of the container as part of its "ssh_info" action. Vagrant::LXC::Action.action_fetch_ip checks the machine state using Builtin::IsState, which calls Vagrant::Machine.state, which also updates the state in the machine index and acquires a machine index entry lock to do that. A race condition ensues in WaitForCommunicator.call, where ready_thr tries to acquire the machine index lock while running ssh_info, and states_thr tries to acquire the same lock doing its own state look up (env[:machine].state.id). If they both try to acquire the lock at the same time, one will fail, and an exception will be raised. Work around this issue by checking for the desired machine state (:running) in Vagrant::LXC::Provider.ssh_info, which can get the state from Vagrant::LXC::Provider.state, which in turn does not write out the state into the index file and does not acquire the index entry lock.
252 lines
8.5 KiB
Ruby
252 lines
8.5 KiB
Ruby
require 'vagrant-lxc/action/boot'
|
|
require 'vagrant-lxc/action/clear_forwarded_ports'
|
|
require 'vagrant-lxc/action/create'
|
|
require 'vagrant-lxc/action/destroy'
|
|
require 'vagrant-lxc/action/destroy_confirm'
|
|
require 'vagrant-lxc/action/compress_rootfs'
|
|
require 'vagrant-lxc/action/fetch_ip_with_lxc_attach'
|
|
require 'vagrant-lxc/action/fetch_ip_from_dnsmasq_leases'
|
|
require 'vagrant-lxc/action/forced_halt'
|
|
require 'vagrant-lxc/action/forward_ports'
|
|
require 'vagrant-lxc/action/handle_box_metadata'
|
|
require 'vagrant-lxc/action/prepare_nfs_settings'
|
|
require 'vagrant-lxc/action/prepare_nfs_valid_ids'
|
|
require 'vagrant-lxc/action/remove_temporary_files'
|
|
require 'vagrant-lxc/action/setup_package_files'
|
|
require 'vagrant-lxc/action/warn_networks'
|
|
|
|
unless Vagrant::Backports.vagrant_1_3_or_later?
|
|
require 'vagrant-backports/action/wait_for_communicator'
|
|
end
|
|
unless Vagrant::Backports.vagrant_1_5_or_later?
|
|
require 'vagrant-backports/ui'
|
|
require 'vagrant-backports/action/handle_box'
|
|
require 'vagrant-backports/action/message'
|
|
require 'vagrant-backports/action/is_state'
|
|
end
|
|
|
|
module Vagrant
|
|
module LXC
|
|
module Action
|
|
# Shortcuts
|
|
Builtin = Vagrant::Action::Builtin
|
|
Builder = Vagrant::Action::Builder
|
|
|
|
# This action is responsible for reloading the machine, which
|
|
# brings it down, sucks in new configuration, and brings the
|
|
# machine back up with the new configuration.
|
|
def self.action_reload
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
|
|
if env1[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use Builtin::ConfigValidate
|
|
b2.use action_halt
|
|
b2.use action_start
|
|
end
|
|
end
|
|
end
|
|
|
|
# This action boots the VM, assuming the VM is in a state that requires
|
|
# a bootup (i.e. not saved).
|
|
def self.action_boot
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Provision
|
|
b.use Builtin::EnvSet, :port_collision_repair => true
|
|
b.use Builtin::HandleForwardedPortCollisions
|
|
if Vagrant::Backports.vagrant_1_4_or_later?
|
|
b.use PrepareNFSValidIds
|
|
b.use Builtin::SyncedFolderCleanup
|
|
b.use Builtin::SyncedFolders
|
|
b.use PrepareNFSSettings
|
|
else
|
|
require 'vagrant-lxc/backports/action/share_folders'
|
|
b.use ShareFolders
|
|
end
|
|
b.use Builtin::SetHostname
|
|
b.use WarnNetworks
|
|
b.use ForwardPorts
|
|
b.use Boot
|
|
b.use Builtin::WaitForCommunicator
|
|
end
|
|
end
|
|
|
|
# This action just runs the provisioners on the machine.
|
|
def self.action_provision
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::ConfigValidate
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
|
|
if env1[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use Builtin::Call, Builtin::IsState, :running do |env2, b3|
|
|
if !env2[:result]
|
|
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_running")
|
|
next
|
|
end
|
|
|
|
b3.use Builtin::Provision
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This action starts a container, assuming it is already created and exists.
|
|
# A precondition of this action is that the container exists.
|
|
def self.action_start
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::ConfigValidate
|
|
b.use Builtin::BoxCheckOutdated
|
|
b.use Builtin::Call, Builtin::IsState, :running do |env, b2|
|
|
# If the VM is running, then our work here is done, exit
|
|
next if env[:result]
|
|
b2.use action_boot
|
|
end
|
|
end
|
|
end
|
|
|
|
# This action brings the machine up from nothing, including creating the
|
|
# container, configuring metadata, and booting.
|
|
def self.action_up
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::ConfigValidate
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
|
|
# If the VM is NOT created yet, then do the setup steps
|
|
if env[:result]
|
|
b2.use Builtin::HandleBox
|
|
b2.use HandleBoxMetadata
|
|
b2.use Create
|
|
end
|
|
end
|
|
b.use action_start
|
|
end
|
|
end
|
|
|
|
# This is the action that is primarily responsible for halting
|
|
# the virtual machine, gracefully or by force.
|
|
def self.action_halt
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
|
|
if env[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use ClearForwardedPorts
|
|
b2.use RemoveTemporaryFiles
|
|
b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
|
|
if !env2[:result]
|
|
b3.use ForcedHalt
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This is the action that is primarily responsible for completely
|
|
# freeing the resources of the underlying virtual machine.
|
|
def self.action_destroy
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
|
|
if env1[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
# TODO: Use Vagrant's built in action once we drop support for vagrant 1.2
|
|
b2.use Builtin::Call, DestroyConfirm do |env2, b3|
|
|
if env2[:result]
|
|
b3.use Builtin::ConfigValidate
|
|
b3.use Builtin::EnvSet, :force_halt => true
|
|
b3.use action_halt
|
|
b3.use Destroy
|
|
if Vagrant::Backports.vagrant_1_3_or_later?
|
|
b3.use Builtin::ProvisionerCleanup
|
|
end
|
|
else
|
|
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.will_not_destroy")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This action packages the virtual machine into a single box file.
|
|
def self.action_package
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
|
|
if env1[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use action_halt
|
|
b2.use CompressRootFS
|
|
b2.use SetupPackageFiles
|
|
b2.use Vagrant::Action::General::Package
|
|
end
|
|
end
|
|
end
|
|
|
|
# This action is called to read the IP of the container. The IP found
|
|
# is expected to be put into the `:machine_ip` key.
|
|
def self.action_fetch_ip
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::Call, Builtin::ConfigValidate do |env, b2|
|
|
b2.use FetchIpWithLxcAttach if env[:machine].provider.driver.supports_attach?
|
|
b2.use FetchIpFromDnsmasqLeases
|
|
end
|
|
end
|
|
end
|
|
|
|
# This is the action that will exec into an SSH shell.
|
|
def self.action_ssh
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::ConfigValidate
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
|
|
if env[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use Builtin::Call, Builtin::IsState, :running do |env1, b3|
|
|
if !env1[:result]
|
|
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_running")
|
|
next
|
|
end
|
|
|
|
b3.use Builtin::SSHExec
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
# This is the action that will run a single SSH command.
|
|
def self.action_ssh_run
|
|
Builder.new.tap do |b|
|
|
b.use Builtin::ConfigValidate
|
|
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
|
|
if env[:result]
|
|
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
|
|
next
|
|
end
|
|
|
|
b2.use Builtin::Call, Builtin::IsState, :running do |env1, b3|
|
|
if !env1[:result]
|
|
raise Vagrant::Errors::VMNotRunningError
|
|
next
|
|
end
|
|
|
|
b3.use Builtin::SSHRun
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|