Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New provider: Docker #3347

Merged
merged 17 commits into from
Apr 10, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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