Skip to content

Commit

Permalink
Merge pull request #3347 from mitchellh/f-docker-provider
Browse files Browse the repository at this point in the history
New provider: Docker
  • Loading branch information
mitchellh committed Apr 10, 2014
2 parents b96ce2a + c7884a7 commit 7b42fcf
Show file tree
Hide file tree
Showing 31 changed files with 1,017 additions and 14 deletions.
195 changes: 195 additions & 0 deletions plugins/providers/docker/action.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
module VagrantPlugins
module DockerProvider
module Action
# Include the built-in modules so we can use them as top-level things.
include Vagrant::Action::Builtin

# This action brings the "machine" up from nothing, including creating the
# container, configuring metadata, and booting.
def self.action_up
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsState, :not_created do |env, b2|
# If the VM is NOT created yet, then do the setup steps
if env[:result]
b2.use HandleBox
b2.use EnvSet, :port_collision_repair => true
b2.use HandleForwardedPortCollisions
b2.use Provision
b2.use PrepareNFSValidIds
b2.use SyncedFolderCleanup
b2.use SyncedFolders
b2.use PrepareNFSSettings
b2.use ForwardPorts
# This will actually create and start, but that's fine
b2.use Create
b2.use action_boot
else
b2.use PrepareNFSValidIds
b2.use SyncedFolderCleanup
b2.use SyncedFolders
b2.use PrepareNFSSettings
b2.use action_start
end
end
end
end

# This action just runs the provisioners on the machine.
def self.action_provision
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use Call, IsState, :running do |env2, b3|
if !env2[:result]
b3.use Message, I18n.t("docker_provider.messages.not_running")
next
end

b3.use Provision
end
end
end
end

# This is the action that is primarily responsible for halting
# the virtual machine, gracefully or by force.
def self.action_halt
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use Call, GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result]
b3.use Stop
end
end
end
end
end

# 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
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use ConfigValidate
b2.use action_halt
b2.use action_start
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
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use Call, DestroyConfirm do |env2, b3|
if env2[:result]
b3.use ConfigValidate
b3.use EnvSet, :force_halt => true
b3.use action_halt
b3.use Destroy
b3.use ProvisionerCleanup
else
b3.use Message, I18n.t("docker_provider.messages.will_not_destroy")
end
end
end
end
end

# This is the action that will exec into an SSH shell.
def self.action_ssh
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use Call, IsState, :running do |env2, b3|
if !env2[:result]
b3.use Message, I18n.t("docker_provider.messages.not_running")
next
end
b3.use SSHExec
end
end
end
end

# This is the action that will run a single SSH command.
def self.action_ssh_run
Vagrant::Action::Builder.new.tap do |b|
b.use Call, IsState, :not_created do |env, b2|
if env[:result]
b2.use Message, I18n.t("docker_provider.messages.not_created")
next
end

b2.use Call, IsState, :running do |env2, b3|
if !env2[:result]
raise Vagrant::Errors::VMNotRunningError
end

b3.use SSHRun
end
end
end
end

def self.action_start
Vagrant::Action::Builder.new.tap do |b|
b.use ConfigValidate
b.use Call, IsState, :running do |env, b2|
# If the container is running, then our work here is done, exit
next if env[:result]

b2.use Provision
b2.use Message, I18n.t("docker_provider.messages.starting")
b2.use action_boot
end
end
end

def self.action_boot
Vagrant::Action::Builder.new.tap do |b|
# TODO: b.use SetHostname
b.use Start
b.use WaitForCommunicator
end
end

# The autoload farm
action_root = Pathname.new(File.expand_path("../action", __FILE__))
autoload :Create, action_root.join("create")
autoload :Destroy, action_root.join("destroy")
autoload :ForwardPorts, action_root.join("forward_ports")
autoload :Stop, action_root.join("stop")
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
autoload :Start, action_root.join("start")
end
end
end
59 changes: 59 additions & 0 deletions plugins/providers/docker/action/create.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
module VagrantPlugins
module DockerProvider
module Action
class Create
def initialize(app, env)
@app = app
@@mutex ||= Mutex.new
end

def call(env)
@env = env
@machine = env[:machine]
@provider_config = @machine.provider_config
@machine_config = @machine.config
@driver = @machine.provider.driver

guard_cmd_configured!

cid = ''
@@mutex.synchronize do
cid = @driver.create(create_params)
end

@machine.id = cid
@app.call(env)
end

def create_params
container_name = "#{@env[:root_path].basename.to_s}_#{@machine.name}"
container_name.gsub!(/[^-a-z0-9_]/i, "")
container_name << "_#{Time.now.to_i}"

{
image: @provider_config.image,
cmd: @provider_config.cmd,
ports: forwarded_ports,
name: container_name,
hostname: @machine_config.vm.hostname,
volumes: @provider_config.volumes,
privileged: @provider_config.privileged
}
end

def forwarded_ports
@env[:forwarded_ports].map do |fp|
# TODO: Support for the protocol argument
"#{fp[:host]}:#{fp[:guest]}"
end.compact
end

def guard_cmd_configured!
if ! @provider_config.image
raise Errors::ImageNotConfiguredError, name: @machine.name
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions plugins/providers/docker/action/destroy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module VagrantPlugins
module DockerProvider
module Action
class Destroy
def initialize(app, env)
@app = app
end

def call(env)
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")

machine = env[:machine]
config = machine.provider_config
driver = machine.provider.driver

driver.rm(machine.id)
machine.id = nil

@app.call env
end
end
end
end
end
54 changes: 54 additions & 0 deletions plugins/providers/docker/action/forward_ports.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module VagrantPlugins
module DockerProvider
module Action
class ForwardPorts
def initialize(app, env)
@app = app
end

def call(env)
@env = env

env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)

if env[:forwarded_ports].any?
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
inform_forwarded_ports(env[:forwarded_ports])
end

# FIXME: Check whether the container has already been created with
# different exposed ports and let the user know about it

@app.call env
end

def inform_forwarded_ports(ports)
ports.each do |fp|
message_attributes = {
:adapter => 'eth0',
:guest_port => fp[:guest],
:host_port => fp[:host]
}

@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
message_attributes))
end
end

private

def compile_forwarded_ports(config)
mappings = {}

config.vm.networks.each do |type, options|
if type == :forwarded_port && options[:id] != 'ssh'
mappings[options[:host]] = options
end
end

mappings.values
end
end
end
end
end
57 changes: 57 additions & 0 deletions plugins/providers/docker/action/prepare_nfs_settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module VagrantPlugins
module DockerProvider
module Action
class PrepareNFSSettings
include Vagrant::Util::Retryable

def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
end

def call(env)
@machine = env[:machine]

@app.call(env)

if using_nfs? && !privileged_container?
raise Errors::NfsWithoutPrivilegedError
end

if using_nfs?
@logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
add_ips_to_env!(env)
end
end

# We're using NFS if we have any synced folder with NFS configured. If
# we are not using NFS we don't need to do the extra work to
# populate these fields in the environment.
def using_nfs?
@machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
end

def privileged_container?
@machine.provider.driver.privileged?(@machine.id)
end

# Extracts the proper host and guest IPs for NFS mounts and stores them
# in the environment for the SyncedFolder action to use them in
# mounting.
#
# The ! indicates that this method modifies its argument.
def add_ips_to_env!(env)
provider = env[:machine].provider

host_ip = provider.driver.docker_bridge_ip
machine_ip = provider.ssh_info[:host]

raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip

env[:nfs_host_ip] = host_ip
env[:nfs_machine_ip] = machine_ip
end
end
end
end
end
Loading

0 comments on commit 7b42fcf

Please sign in to comment.