providers/docker: support building Dockerfiles
This commit is contained in:
parent
3ba0ca3562
commit
81df70eee0
|
@ -34,7 +34,6 @@ module Vagrant
|
||||||
# If this is our scope, then override
|
# If this is our scope, then override
|
||||||
if parts[0] == scope
|
if parts[0] == scope
|
||||||
result[parts[1].to_sym] = value
|
result[parts[1].to_sym] = value
|
||||||
result.delete(key)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ module VagrantPlugins
|
||||||
|
|
||||||
b2.use ConfigValidate
|
b2.use ConfigValidate
|
||||||
b2.use action_halt
|
b2.use action_halt
|
||||||
|
b2.use EnvSet, build_rebuild: true
|
||||||
b2.use action_start
|
b2.use action_start
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -202,18 +203,18 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# We only want to actually sync folder differences if
|
|
||||||
# we're not created.
|
|
||||||
b2.use Call, IsState, :not_created do |env2, b3|
|
b2.use Call, IsState, :not_created do |env2, b3|
|
||||||
if !env2[:result]
|
if !env2[:result]
|
||||||
b3.use EnvSet, host_machine_sync_folders: false
|
b3.use EnvSet, host_machine_sync_folders: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
b2.use HostMachineBuildDir
|
||||||
b2.use HostMachineSyncFolders
|
b2.use HostMachineSyncFolders
|
||||||
b2.use PrepareNFSValidIds
|
b2.use PrepareNFSValidIds
|
||||||
b2.use SyncedFolderCleanup
|
b2.use SyncedFolderCleanup
|
||||||
b2.use PrepareNFSSettings
|
b2.use PrepareNFSSettings
|
||||||
|
b2.use Build
|
||||||
|
|
||||||
# If the VM is NOT created yet, then do some setup steps
|
# If the VM is NOT created yet, then do some setup steps
|
||||||
# necessary for creating it.
|
# necessary for creating it.
|
||||||
|
@ -245,11 +246,13 @@ module VagrantPlugins
|
||||||
|
|
||||||
# The autoload farm
|
# The autoload farm
|
||||||
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
action_root = Pathname.new(File.expand_path("../action", __FILE__))
|
||||||
|
autoload :Build, action_root.join("build")
|
||||||
autoload :CompareSyncedFolders, action_root.join("compare_synced_folders")
|
autoload :CompareSyncedFolders, action_root.join("compare_synced_folders")
|
||||||
autoload :Create, action_root.join("create")
|
autoload :Create, action_root.join("create")
|
||||||
autoload :Destroy, action_root.join("destroy")
|
autoload :Destroy, action_root.join("destroy")
|
||||||
autoload :HasSSH, action_root.join("has_ssh")
|
autoload :HasSSH, action_root.join("has_ssh")
|
||||||
autoload :HostMachine, action_root.join("host_machine")
|
autoload :HostMachine, action_root.join("host_machine")
|
||||||
|
autoload :HostMachineBuildDir, action_root.join("host_machine_build_dir")
|
||||||
autoload :HostMachinePortChecker, action_root.join("host_machine_port_checker")
|
autoload :HostMachinePortChecker, action_root.join("host_machine_port_checker")
|
||||||
autoload :HostMachinePortWarning, action_root.join("host_machine_port_warning")
|
autoload :HostMachinePortWarning, action_root.join("host_machine_port_warning")
|
||||||
autoload :HostMachineRequired, action_root.join("host_machine_required")
|
autoload :HostMachineRequired, action_root.join("host_machine_required")
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
require "log4r"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Action
|
||||||
|
class Build
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::docker::build")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
machine = env[:machine]
|
||||||
|
build_dir = env[:build_dir]
|
||||||
|
build_dir ||= machine.provider_config.build_dir
|
||||||
|
|
||||||
|
# If we're not building a container, then just skip this step
|
||||||
|
return @app.call(env) if !build_dir
|
||||||
|
|
||||||
|
# Try to read the image ID from the cache file if we've
|
||||||
|
# already built it.
|
||||||
|
image_file = machine.data_dir.join("docker_build_image")
|
||||||
|
image = nil
|
||||||
|
if image_file.file?
|
||||||
|
image = image_file.read.chomp
|
||||||
|
end
|
||||||
|
|
||||||
|
# If we have no image or we're rebuilding, we rebuild
|
||||||
|
if !image || env[:build_rebuild]
|
||||||
|
# Build it
|
||||||
|
machine.ui.output(I18n.t("docker_provider.building"))
|
||||||
|
image = machine.provider.driver.build(build_dir)
|
||||||
|
|
||||||
|
# Store the image ID
|
||||||
|
image_file.open("w") do |f|
|
||||||
|
f.binmode
|
||||||
|
f.write("#{image}\n")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
machine.ui.output(I18n.t("docker_provider.already_built"))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set the image for creation
|
||||||
|
env[:create_image] = image
|
||||||
|
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -50,6 +50,9 @@ module VagrantPlugins
|
||||||
container_name << "_#{Time.now.to_i}"
|
container_name << "_#{Time.now.to_i}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
image = @env[:create_image]
|
||||||
|
image ||= @provider_config.image
|
||||||
|
|
||||||
links = {}
|
links = {}
|
||||||
@provider_config._links.each do |link|
|
@provider_config._links.each do |link|
|
||||||
parts = link.split(":", 2)
|
parts = link.split(":", 2)
|
||||||
|
@ -61,7 +64,7 @@ module VagrantPlugins
|
||||||
env: @provider_config.env,
|
env: @provider_config.env,
|
||||||
extra_args: @provider_config.create_args,
|
extra_args: @provider_config.create_args,
|
||||||
hostname: @machine_config.vm.hostname,
|
hostname: @machine_config.vm.hostname,
|
||||||
image: @provider_config.image,
|
image: image,
|
||||||
links: links,
|
links: links,
|
||||||
name: container_name,
|
name: container_name,
|
||||||
ports: forwarded_ports,
|
ports: forwarded_ports,
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
require "digest/md5"
|
||||||
|
|
||||||
|
require "log4r"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module DockerProvider
|
||||||
|
module Action
|
||||||
|
class HostMachineBuildDir
|
||||||
|
def initialize(app, env)
|
||||||
|
@app = app
|
||||||
|
@logger = Log4r::Logger.new("vagrant::docker::hostmachinebuilddir")
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
machine = env[:machine]
|
||||||
|
build_dir = machine.provider_config.build_dir
|
||||||
|
|
||||||
|
# If we're not building a Dockerfile, ignore
|
||||||
|
return @app.call(env) if !build_dir
|
||||||
|
|
||||||
|
# If we're building a docker file, expand the directory
|
||||||
|
build_dir = File.expand_path(build_dir, env[:machine].env.root_path)
|
||||||
|
env[:build_dir] = build_dir
|
||||||
|
|
||||||
|
# If we're not on a host VM, we're done
|
||||||
|
return @app.call(env) if !machine.provider.host_vm?
|
||||||
|
|
||||||
|
# We're on a host VM, so we need to move our build dir to
|
||||||
|
# that machine. We do this by putting the synced folder on
|
||||||
|
# ourself and letting HostMachineSyncFolders handle it.
|
||||||
|
new_build_dir = "/mnt/docker_build_#{Digest::MD5.hexdigest(build_dir)}"
|
||||||
|
options = {
|
||||||
|
docker__ignore: true,
|
||||||
|
docker__exact: true,
|
||||||
|
}
|
||||||
|
machine.config.vm.synced_folder(build_dir, new_build_dir, options)
|
||||||
|
|
||||||
|
# Set the build dir to be the correct one
|
||||||
|
env[:build_dir] = new_build_dir
|
||||||
|
|
||||||
|
@app.call(env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -91,15 +91,23 @@ module VagrantPlugins
|
||||||
# Generate an ID that is deterministic based on our machine
|
# Generate an ID that is deterministic based on our machine
|
||||||
# and Vagrantfile path...
|
# and Vagrantfile path...
|
||||||
id = Digest::MD5.hexdigest(
|
id = Digest::MD5.hexdigest(
|
||||||
"#{env[:machine].env.root_path}#{env[:machine].name}")
|
"#{env[:machine].env.root_path}" +
|
||||||
|
"#{data[:hostpath]}" +
|
||||||
|
"#{data[:guestpath]}" +
|
||||||
|
"#{env[:machine].name}")
|
||||||
|
|
||||||
# Generate a new guestpath
|
# Generate a new guestpath
|
||||||
data[:docker_guestpath] = data[:guestpath]
|
data[:docker_guestpath] = data[:guestpath]
|
||||||
data[:docker_sfid] = id
|
data[:docker_sfid] = id
|
||||||
data[:docker_host_sfid] = host_sfid
|
data[:docker_host_sfid] = host_sfid
|
||||||
data[:guestpath] = "/mnt/docker_#{Time.now.to_i}_#{rand(100000)}"
|
|
||||||
data[:id] = id[0...6] + rand(10000).to_s
|
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] =
|
||||||
|
"/mnt/docker_#{Time.now.to_i}_#{rand(100000)}"
|
||||||
|
end
|
||||||
|
|
||||||
# Add this synced folder onto the new config if we haven't
|
# Add this synced folder onto the new config if we haven't
|
||||||
# already shared it before.
|
# already shared it before.
|
||||||
if !existing_ids.has_key?(id)
|
if !existing_ids.has_key?(id)
|
||||||
|
|
|
@ -3,6 +3,12 @@ module VagrantPlugins
|
||||||
class Config < Vagrant.plugin("2", :config)
|
class Config < Vagrant.plugin("2", :config)
|
||||||
attr_accessor :image, :cmd, :ports, :volumes, :privileged
|
attr_accessor :image, :cmd, :ports, :volumes, :privileged
|
||||||
|
|
||||||
|
# The directory with a Dockerfile to build and use as the basis
|
||||||
|
# for this container. If this is set, "image" should not be set.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :build_dir
|
||||||
|
|
||||||
# Additional arguments to pass to `docker run` when creating
|
# Additional arguments to pass to `docker run` when creating
|
||||||
# the container for the first time. This is an array of args.
|
# the container for the first time. This is an array of args.
|
||||||
#
|
#
|
||||||
|
@ -54,6 +60,7 @@ module VagrantPlugins
|
||||||
attr_accessor :vagrant_vagrantfile
|
attr_accessor :vagrant_vagrantfile
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
@build_dir = UNSET_VALUE
|
||||||
@cmd = UNSET_VALUE
|
@cmd = UNSET_VALUE
|
||||||
@create_args = []
|
@create_args = []
|
||||||
@env = {}
|
@env = {}
|
||||||
|
@ -87,6 +94,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize!
|
def finalize!
|
||||||
|
@build_dir = nil if @build_dir == UNSET_VALUE
|
||||||
@cmd = [] if @cmd == UNSET_VALUE
|
@cmd = [] if @cmd == UNSET_VALUE
|
||||||
@create_args = [] if @create_args == UNSET_VALUE
|
@create_args = [] if @create_args == UNSET_VALUE
|
||||||
@env ||= {}
|
@env ||= {}
|
||||||
|
|
|
@ -14,6 +14,17 @@ module VagrantPlugins
|
||||||
@executor = Executor::Local.new
|
@executor = Executor::Local.new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def build(dir, **opts)
|
||||||
|
result = execute('docker', 'build', dir)
|
||||||
|
regexp = /Successfully built (.+)$/i
|
||||||
|
match = regexp.match(result)
|
||||||
|
if !match
|
||||||
|
# TODO: error
|
||||||
|
end
|
||||||
|
|
||||||
|
match[1]
|
||||||
|
end
|
||||||
|
|
||||||
def create(params)
|
def create(params)
|
||||||
image = params.fetch(:image)
|
image = params.fetch(:image)
|
||||||
links = params.fetch(:links)
|
links = params.fetch(:links)
|
||||||
|
|
|
@ -16,9 +16,9 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare(machine, folders, _opts)
|
def prepare(machine, folders, _opts)
|
||||||
# FIXME: Check whether the container has already been created with
|
|
||||||
# different synced folders and let the user know about it
|
|
||||||
folders.each do |id, data|
|
folders.each do |id, data|
|
||||||
|
next if data[:ignore]
|
||||||
|
|
||||||
host_path = data[:hostpath]
|
host_path = data[:hostpath]
|
||||||
guest_path = data[:guestpath]
|
guest_path = data[:guestpath]
|
||||||
machine.provider_config.volumes << "#{host_path}:#{guest_path}"
|
machine.provider_config.volumes << "#{host_path}:#{guest_path}"
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
en:
|
en:
|
||||||
docker_provider:
|
docker_provider:
|
||||||
|
already_built: |-
|
||||||
|
Image is already built from the Dockerfile. `vagrant reload` to rebuild.
|
||||||
|
building: |-
|
||||||
|
Building the container from a Dockerfile...
|
||||||
creating: |-
|
creating: |-
|
||||||
Creating the container...
|
Creating the container...
|
||||||
created: |-
|
created: |-
|
||||||
|
|
|
@ -24,6 +24,7 @@ describe VagrantPlugins::DockerProvider::Config do
|
||||||
describe "defaults" do
|
describe "defaults" do
|
||||||
before { subject.finalize! }
|
before { subject.finalize! }
|
||||||
|
|
||||||
|
its(:build_dir) { should be_nil }
|
||||||
its(:cmd) { should eq([]) }
|
its(:cmd) { should eq([]) }
|
||||||
its(:env) { should eq({}) }
|
its(:env) { should eq({}) }
|
||||||
its(:image) { should be_nil }
|
its(:image) { should be_nil }
|
||||||
|
|
|
@ -25,7 +25,8 @@ describe Vagrant::Util::ScopedHashOverride do
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
:key => "replaced"
|
:key => "replaced",
|
||||||
|
:scope__key => "replaced"
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(klass.scoped_hash_override(original, "scope")).to eq(expected)
|
expect(klass.scoped_hash_override(original, "scope")).to eq(expected)
|
||||||
|
@ -40,6 +41,7 @@ describe Vagrant::Util::ScopedHashOverride do
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
:key => "replaced",
|
:key => "replaced",
|
||||||
|
:scope__key => "replaced",
|
||||||
:another__key => "value"
|
:another__key => "value"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue