Skip to content

Instantly share code, notes, and snippets.

@scottatron
Created July 27, 2014 05:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scottatron/7e1686a07675e21a7496 to your computer and use it in GitHub Desktop.
Save scottatron/7e1686a07675e21a7496 to your computer and use it in GitHub Desktop.
Vagrant Docker host VM with NFS support
require "digest/md5"
require "securerandom"
require "log4r"
require "vagrant/action/builtin/mixin_synced_folders"
require Vagrant.source_root.join("plugins/providers/docker/action/host_machine_sync_folders.rb")
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 HostMachineSyncFolders
include Vagrant::Action::Builtin::MixinSyncedFolders
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::docker::hostmachine")
end
def call(env)
return @app.call(env) if !env[:machine].provider.host_vm?
if !env.has_key?(:host_machine_sync_folders)
env[:host_machine_sync_folders] = true
end
host_machine = env[:machine].provider.host_vm
# Lock while we make changes
begin
env[:machine].provider.host_vm_lock do
setup_synced_folders(host_machine, env)
end
rescue Vagrant::Errors::EnvironmentLockedError
sleep 1
retry
end
@app.call(env)
end
protected
def setup_synced_folders(host_machine, env)
# Write the host machine SFID if we have one
id_path = host_machine.data_dir.join("host_machine_sfid")
host_sfid = nil
if !id_path.file?
host_sfid = SecureRandom.uuid
id_path.open("w") do |f|
f.binmode
f.write("#{host_sfid}\n")
end
else
host_sfid = id_path.read.chomp
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
# Read the existing folders that are setup
existing_folders = synced_folders(host_machine, cached: true)
existing_ids = {}
if existing_folders
existing_folders.each do |impl, fs|
fs.each do |_name, data|
if data[:docker_sfid] && data[:docker_host_sfid] == host_sfid
existing_ids[data[:docker_sfid]] = data
end
end
end
end
host_vm_build_dir_options = env[:machine].provider_config.host_vm_build_dir_options || {}
# Sync some folders so that our volumes work later.
new_config = VagrantPlugins::Kernel_V2::VMConfig.new
our_folders = synced_folders(env[:machine])
our_folders.each do |type, folders|
folders.each do |id, data|
data = data.dup
if type == :docker
# We don't use the Docker type explicitly on the host VM
data.delete(:type)
end
# Expand the hostpath relative to _our_ root path. Otherwise,
# it expands it relative to the proxy VM, which is not what
# we want.
data[:hostpath] = File.expand_path(
data[:hostpath], env[:machine].env.root_path)
# Generate an ID that is deterministic based on our machine
# and Vagrantfile path...
id = Digest::MD5.hexdigest(
"#{env[:machine].env.root_path}" +
"#{data[:hostpath]}" +
"#{data[:guestpath]}" +
"#{env[:machine].name}")
# Generate a new guestpath
data[:docker_guestpath] = data[:guestpath]
data[:docker_sfid] = id
data[:docker_host_sfid] = host_sfid
data[:id] = id[0...6] + rand(10000).to_s
# If we specify exact then we know what we're doing
if !data[:docker__exact]
data[:guestpath] =
"/var/lib/docker/docker_#{Time.now.to_i}_#{rand(100000)}"
end
if host_vm_build_dir_options[:type] && host_vm_build_dir_options[:type] == "nfs" &&
host_vm_build_dir_options[:mount_options]
data[:mount_options] = host_vm_build_dir_options[:mount_options]
end
# Add this synced folder onto the new config if we haven't
# already shared it before.
if !existing_ids.has_key?(id)
# A bit of a hack for VirtualBox to mount our
# folder as transient. This can be removed once
# the VirtualBox synced folder mechanism is smarter.
data[:virtualbox__transient] = true
new_config.synced_folder(
data[:hostpath],
data[:guestpath],
data)
else
# We already have the folder, so just load its data
data = existing_ids[id]
end
# Remove from our machine
env[:machine].config.vm.synced_folders.delete(id)
# Add the "fixed" folder to our machine
data = data.merge({
hostpath_exact: true,
type: :docker,
})
env[:machine].config.vm.synced_folder(
data[:guestpath],
data[:docker_guestpath],
data)
end
end
if !env[:host_machine_sync_folders]
@logger.info("Not syncing folders because container created.")
return
end
action_env = { synced_folders_config: new_config }
# Prepare NFS
if host_vm_build_dir_options[:type] && host_vm_build_dir_options[:type] == "nfs"
nfs_host_ip = nil
host_machine.provider.driver.read_network_interfaces.each do |adapter, opts|
if opts[:type] == :hostonly
host_machine.provider.driver.read_host_only_interfaces.each do |interface|
if interface[:name] == opts[:hostonly]
nfs_host_ip = interface[:ip]
end
end
end
end
action_env[:nfs_host_ip] = nfs_host_ip
nfs_machine_ip = nil
host_machine.config.vm.networks.each do |type, options|
if type == :private_network && options[:ip].is_a?(String)
nfs_machine_ip = options[:ip]
end
end
action_env[:nfs_machine_ip] = nfs_machine_ip
end
if !new_config.synced_folders.empty?
# Sync the folders!
env[:machine].ui.output(I18n.t(
"docker_provider.host_machine_syncing_folders"))
host_machine.with_ui(proxy_ui) do
begin
host_machine.action(:sync_folders, action_env)
rescue Vagrant::Errors::MachineActionLockedError
sleep 1
retry
rescue Vagrant::Errors::UnimplementedProviderAction
callable = Vagrant::Action::Builder.new
callable.use Vagrant::Action::Builtin::SyncedFolders
host_machine.action_raw(:sync_folders, callable, action_env)
end
end
end
end
end
end
end
end
require_relative "host_machine_sync_folders-patched.rb"
Vagrant.configure("2") do |config|
config.vm.define "docker-host"
# Has patches for NFS
# http://vagrantcloud.com/yungsang/boot2docker
config.vm.box = "yungsang/boot2docker"
config.vm.provider "virtualbox" do |v, override|
# On VirtualBox, we don't have guest additions or a functional vboxsf
# in TinyCore Linux, so tell Vagrant that so it can be smarter.
v.check_guest_additions = false
v.functional_vboxsf = false
override.vm.network "private_network", ip: "192.168.33.10"
end
# Disabled because we don't actually need it for the Docker host VM.
config.vm.synced_folder ".", "/vagrant", disabled: true
# Change TinyCore Linux mirror as repo.tinycorelinux.net doesn't work for me in Australia
# Full list of mirrors at: http://wiki.tinycorelinux.net/wiki:mirrors
config.vm.provision 'shell', inline: 'echo http://tinycore.mirror.uber.com.au > /opt/tcemirror'
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment