Skip to content

Commit 7b42fcf

Browse files
committed
Merge pull request #3347 from mitchellh/f-docker-provider
New provider: Docker
2 parents b96ce2a + c7884a7 commit 7b42fcf

31 files changed

+1017
-14
lines changed

plugins/providers/docker/action.rb

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
module VagrantPlugins
2+
module DockerProvider
3+
module Action
4+
# Include the built-in modules so we can use them as top-level things.
5+
include Vagrant::Action::Builtin
6+
7+
# This action brings the "machine" up from nothing, including creating the
8+
# container, configuring metadata, and booting.
9+
def self.action_up
10+
Vagrant::Action::Builder.new.tap do |b|
11+
b.use ConfigValidate
12+
b.use Call, IsState, :not_created do |env, b2|
13+
# If the VM is NOT created yet, then do the setup steps
14+
if env[:result]
15+
b2.use HandleBox
16+
b2.use EnvSet, :port_collision_repair => true
17+
b2.use HandleForwardedPortCollisions
18+
b2.use Provision
19+
b2.use PrepareNFSValidIds
20+
b2.use SyncedFolderCleanup
21+
b2.use SyncedFolders
22+
b2.use PrepareNFSSettings
23+
b2.use ForwardPorts
24+
# This will actually create and start, but that's fine
25+
b2.use Create
26+
b2.use action_boot
27+
else
28+
b2.use PrepareNFSValidIds
29+
b2.use SyncedFolderCleanup
30+
b2.use SyncedFolders
31+
b2.use PrepareNFSSettings
32+
b2.use action_start
33+
end
34+
end
35+
end
36+
end
37+
38+
# This action just runs the provisioners on the machine.
39+
def self.action_provision
40+
Vagrant::Action::Builder.new.tap do |b|
41+
b.use ConfigValidate
42+
b.use Call, IsState, :not_created do |env, b2|
43+
if env[:result]
44+
b2.use Message, I18n.t("docker_provider.messages.not_created")
45+
next
46+
end
47+
48+
b2.use Call, IsState, :running do |env2, b3|
49+
if !env2[:result]
50+
b3.use Message, I18n.t("docker_provider.messages.not_running")
51+
next
52+
end
53+
54+
b3.use Provision
55+
end
56+
end
57+
end
58+
end
59+
60+
# This is the action that is primarily responsible for halting
61+
# the virtual machine, gracefully or by force.
62+
def self.action_halt
63+
Vagrant::Action::Builder.new.tap do |b|
64+
b.use Call, IsState, :not_created do |env, b2|
65+
if env[:result]
66+
b2.use Message, I18n.t("docker_provider.messages.not_created")
67+
next
68+
end
69+
70+
b2.use Call, GracefulHalt, :stopped, :running do |env2, b3|
71+
if !env2[:result]
72+
b3.use Stop
73+
end
74+
end
75+
end
76+
end
77+
end
78+
79+
# This action is responsible for reloading the machine, which
80+
# brings it down, sucks in new configuration, and brings the
81+
# machine back up with the new configuration.
82+
def self.action_reload
83+
Vagrant::Action::Builder.new.tap do |b|
84+
b.use Call, IsState, :not_created do |env, b2|
85+
if env[:result]
86+
b2.use Message, I18n.t("docker_provider.messages.not_created")
87+
next
88+
end
89+
90+
b2.use ConfigValidate
91+
b2.use action_halt
92+
b2.use action_start
93+
end
94+
end
95+
end
96+
97+
# This is the action that is primarily responsible for completely
98+
# freeing the resources of the underlying virtual machine.
99+
def self.action_destroy
100+
Vagrant::Action::Builder.new.tap do |b|
101+
b.use Call, IsState, :not_created do |env, b2|
102+
if env[:result]
103+
b2.use Message, I18n.t("docker_provider.messages.not_created")
104+
next
105+
end
106+
107+
b2.use Call, DestroyConfirm do |env2, b3|
108+
if env2[:result]
109+
b3.use ConfigValidate
110+
b3.use EnvSet, :force_halt => true
111+
b3.use action_halt
112+
b3.use Destroy
113+
b3.use ProvisionerCleanup
114+
else
115+
b3.use Message, I18n.t("docker_provider.messages.will_not_destroy")
116+
end
117+
end
118+
end
119+
end
120+
end
121+
122+
# This is the action that will exec into an SSH shell.
123+
def self.action_ssh
124+
Vagrant::Action::Builder.new.tap do |b|
125+
b.use Call, IsState, :not_created do |env, b2|
126+
if env[:result]
127+
b2.use Message, I18n.t("docker_provider.messages.not_created")
128+
next
129+
end
130+
131+
b2.use Call, IsState, :running do |env2, b3|
132+
if !env2[:result]
133+
b3.use Message, I18n.t("docker_provider.messages.not_running")
134+
next
135+
end
136+
b3.use SSHExec
137+
end
138+
end
139+
end
140+
end
141+
142+
# This is the action that will run a single SSH command.
143+
def self.action_ssh_run
144+
Vagrant::Action::Builder.new.tap do |b|
145+
b.use Call, IsState, :not_created do |env, b2|
146+
if env[:result]
147+
b2.use Message, I18n.t("docker_provider.messages.not_created")
148+
next
149+
end
150+
151+
b2.use Call, IsState, :running do |env2, b3|
152+
if !env2[:result]
153+
raise Vagrant::Errors::VMNotRunningError
154+
end
155+
156+
b3.use SSHRun
157+
end
158+
end
159+
end
160+
end
161+
162+
def self.action_start
163+
Vagrant::Action::Builder.new.tap do |b|
164+
b.use ConfigValidate
165+
b.use Call, IsState, :running do |env, b2|
166+
# If the container is running, then our work here is done, exit
167+
next if env[:result]
168+
169+
b2.use Provision
170+
b2.use Message, I18n.t("docker_provider.messages.starting")
171+
b2.use action_boot
172+
end
173+
end
174+
end
175+
176+
def self.action_boot
177+
Vagrant::Action::Builder.new.tap do |b|
178+
# TODO: b.use SetHostname
179+
b.use Start
180+
b.use WaitForCommunicator
181+
end
182+
end
183+
184+
# The autoload farm
185+
action_root = Pathname.new(File.expand_path("../action", __FILE__))
186+
autoload :Create, action_root.join("create")
187+
autoload :Destroy, action_root.join("destroy")
188+
autoload :ForwardPorts, action_root.join("forward_ports")
189+
autoload :Stop, action_root.join("stop")
190+
autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids")
191+
autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings")
192+
autoload :Start, action_root.join("start")
193+
end
194+
end
195+
end
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
module VagrantPlugins
2+
module DockerProvider
3+
module Action
4+
class Create
5+
def initialize(app, env)
6+
@app = app
7+
@@mutex ||= Mutex.new
8+
end
9+
10+
def call(env)
11+
@env = env
12+
@machine = env[:machine]
13+
@provider_config = @machine.provider_config
14+
@machine_config = @machine.config
15+
@driver = @machine.provider.driver
16+
17+
guard_cmd_configured!
18+
19+
cid = ''
20+
@@mutex.synchronize do
21+
cid = @driver.create(create_params)
22+
end
23+
24+
@machine.id = cid
25+
@app.call(env)
26+
end
27+
28+
def create_params
29+
container_name = "#{@env[:root_path].basename.to_s}_#{@machine.name}"
30+
container_name.gsub!(/[^-a-z0-9_]/i, "")
31+
container_name << "_#{Time.now.to_i}"
32+
33+
{
34+
image: @provider_config.image,
35+
cmd: @provider_config.cmd,
36+
ports: forwarded_ports,
37+
name: container_name,
38+
hostname: @machine_config.vm.hostname,
39+
volumes: @provider_config.volumes,
40+
privileged: @provider_config.privileged
41+
}
42+
end
43+
44+
def forwarded_ports
45+
@env[:forwarded_ports].map do |fp|
46+
# TODO: Support for the protocol argument
47+
"#{fp[:host]}:#{fp[:guest]}"
48+
end.compact
49+
end
50+
51+
def guard_cmd_configured!
52+
if ! @provider_config.image
53+
raise Errors::ImageNotConfiguredError, name: @machine.name
54+
end
55+
end
56+
end
57+
end
58+
end
59+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module VagrantPlugins
2+
module DockerProvider
3+
module Action
4+
class Destroy
5+
def initialize(app, env)
6+
@app = app
7+
end
8+
9+
def call(env)
10+
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")
11+
12+
machine = env[:machine]
13+
config = machine.provider_config
14+
driver = machine.provider.driver
15+
16+
driver.rm(machine.id)
17+
machine.id = nil
18+
19+
@app.call env
20+
end
21+
end
22+
end
23+
end
24+
end
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
module VagrantPlugins
2+
module DockerProvider
3+
module Action
4+
class ForwardPorts
5+
def initialize(app, env)
6+
@app = app
7+
end
8+
9+
def call(env)
10+
@env = env
11+
12+
env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
13+
14+
if env[:forwarded_ports].any?
15+
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
16+
inform_forwarded_ports(env[:forwarded_ports])
17+
end
18+
19+
# FIXME: Check whether the container has already been created with
20+
# different exposed ports and let the user know about it
21+
22+
@app.call env
23+
end
24+
25+
def inform_forwarded_ports(ports)
26+
ports.each do |fp|
27+
message_attributes = {
28+
:adapter => 'eth0',
29+
:guest_port => fp[:guest],
30+
:host_port => fp[:host]
31+
}
32+
33+
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
34+
message_attributes))
35+
end
36+
end
37+
38+
private
39+
40+
def compile_forwarded_ports(config)
41+
mappings = {}
42+
43+
config.vm.networks.each do |type, options|
44+
if type == :forwarded_port && options[:id] != 'ssh'
45+
mappings[options[:host]] = options
46+
end
47+
end
48+
49+
mappings.values
50+
end
51+
end
52+
end
53+
end
54+
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
module VagrantPlugins
2+
module DockerProvider
3+
module Action
4+
class PrepareNFSSettings
5+
include Vagrant::Util::Retryable
6+
7+
def initialize(app, env)
8+
@app = app
9+
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
10+
end
11+
12+
def call(env)
13+
@machine = env[:machine]
14+
15+
@app.call(env)
16+
17+
if using_nfs? && !privileged_container?
18+
raise Errors::NfsWithoutPrivilegedError
19+
end
20+
21+
if using_nfs?
22+
@logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
23+
add_ips_to_env!(env)
24+
end
25+
end
26+
27+
# We're using NFS if we have any synced folder with NFS configured. If
28+
# we are not using NFS we don't need to do the extra work to
29+
# populate these fields in the environment.
30+
def using_nfs?
31+
@machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
32+
end
33+
34+
def privileged_container?
35+
@machine.provider.driver.privileged?(@machine.id)
36+
end
37+
38+
# Extracts the proper host and guest IPs for NFS mounts and stores them
39+
# in the environment for the SyncedFolder action to use them in
40+
# mounting.
41+
#
42+
# The ! indicates that this method modifies its argument.
43+
def add_ips_to_env!(env)
44+
provider = env[:machine].provider
45+
46+
host_ip = provider.driver.docker_bridge_ip
47+
machine_ip = provider.ssh_info[:host]
48+
49+
raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip
50+
51+
env[:nfs_host_ip] = host_ip
52+
env[:nfs_machine_ip] = machine_ip
53+
end
54+
end
55+
end
56+
end
57+
end

0 commit comments

Comments
 (0)