docker-provider 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +21 -0
- data/Gemfile.lock +125 -0
- data/Guardfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +134 -0
- data/Rakefile +20 -0
- data/boxes/dummy/README.md +17 -0
- data/boxes/dummy/metadata.json +3 -0
- data/boxes/nginx/.gitignore +1 -0
- data/boxes/nginx/Dockerfile +4 -0
- data/boxes/nginx/README.md +25 -0
- data/boxes/nginx/Vagrantfile.sample +6 -0
- data/boxes/nginx/metadata.json +3 -0
- data/boxes/nginx/start +5 -0
- data/boxes/precise/.gitignore +1 -0
- data/boxes/precise/Dockerfile +42 -0
- data/boxes/precise/README.md +18 -0
- data/boxes/precise/Vagrantfile.sample +6 -0
- data/boxes/precise/metadata.json +3 -0
- data/development/Vagrantfile +91 -0
- data/docker-provider.gemspec +25 -0
- data/example/Vagrantfile +36 -0
- data/lib/docker-provider.rb +1 -0
- data/lib/docker-provider/action.rb +161 -0
- data/lib/docker-provider/action/check_running.rb +25 -0
- data/lib/docker-provider/action/create.rb +56 -0
- data/lib/docker-provider/action/created.rb +18 -0
- data/lib/docker-provider/action/destroy.rb +24 -0
- data/lib/docker-provider/action/forward_ports.rb +54 -0
- data/lib/docker-provider/action/is_running.rb +20 -0
- data/lib/docker-provider/action/message.rb +23 -0
- data/lib/docker-provider/action/share_folders.rb +63 -0
- data/lib/docker-provider/action/start.rb +18 -0
- data/lib/docker-provider/action/stop.rb +21 -0
- data/lib/docker-provider/config.rb +28 -0
- data/lib/docker-provider/driver.rb +114 -0
- data/lib/docker-provider/plugin.rb +24 -0
- data/lib/docker-provider/provider.rb +59 -0
- data/lib/docker-provider/version.rb +5 -0
- data/locales/en.yml +21 -0
- data/spec/acceptance/Vagrantfile +25 -0
- data/spec/acceptance/vagrant_ssh.bats +34 -0
- data/spec/acceptance/vagrant_up.bats +35 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/unit_example_group.rb +39 -0
- data/spec/unit/driver_spec.rb +143 -0
- metadata +142 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
class Created
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
machine = env[:machine]
|
11
|
+
driver = machine.provider.driver
|
12
|
+
env[:result] = machine.id && driver.created?(machine.id)
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -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
|
@@ -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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
class IsRunning
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
machine = env[:machine]
|
11
|
+
driver = machine.provider.driver
|
12
|
+
|
13
|
+
env[:result] = driver.running?(machine.id)
|
14
|
+
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
# XXX: Is this really needed? Should we contribute this back to Vagrant's core?
|
5
|
+
class Message
|
6
|
+
def initialize(app, env, msg_key, type = :info)
|
7
|
+
@app = app
|
8
|
+
@msg_key = msg_key
|
9
|
+
@type = type
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
machine = env[:machine]
|
14
|
+
message = I18n.t("docker_provider.messages.#{@msg_key}", name: machine.name)
|
15
|
+
|
16
|
+
env[:ui].send @type, message
|
17
|
+
|
18
|
+
@app.call env
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
class ShareFolders
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
@env = env
|
11
|
+
prepare_folders
|
12
|
+
|
13
|
+
# FIXME: Check whether the container has already been created with
|
14
|
+
# different synced folders and let the user know about it
|
15
|
+
folders = []
|
16
|
+
shared_folders.each do |id, data|
|
17
|
+
folders << {
|
18
|
+
:name => id,
|
19
|
+
:hostpath => File.expand_path(data[:hostpath], @env[:root_path]),
|
20
|
+
:guestpath => data[:guestpath]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
@env[:synced_folders] = folders
|
24
|
+
|
25
|
+
@app.call(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
# This method returns an actual list of synced folders to create and their
|
29
|
+
# proper path.
|
30
|
+
def shared_folders
|
31
|
+
{}.tap do |result|
|
32
|
+
@env[:machine].config.vm.synced_folders.each do |id, data|
|
33
|
+
# Ignore disabled shared folders
|
34
|
+
next if data[:disabled]
|
35
|
+
# This to prevent overwriting the actual shared folders data
|
36
|
+
result[id] = data.dup
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Prepares the shared folders by verifying they exist and creating them
|
42
|
+
# if they don't.
|
43
|
+
def prepare_folders
|
44
|
+
shared_folders.each do |id, options|
|
45
|
+
hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
|
46
|
+
|
47
|
+
if !hostpath.directory? && options[:create]
|
48
|
+
# Host path doesn't exist, so let's create it.
|
49
|
+
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
|
50
|
+
|
51
|
+
begin
|
52
|
+
hostpath.mkpath
|
53
|
+
rescue Errno::EACCES
|
54
|
+
raise Vagrant::Errors::SharedFolderCreateFailed,
|
55
|
+
:path => hostpath.to_s
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
class Start
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
machine = env[:machine]
|
11
|
+
driver = machine.provider.driver
|
12
|
+
driver.start(machine.id)
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
module Action
|
4
|
+
class Stop
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
machine = env[:machine]
|
11
|
+
driver = machine.provider.driver
|
12
|
+
if driver.running?(machine.id)
|
13
|
+
env[:ui].info I18n.t("docker_provider.messages.stopping")
|
14
|
+
driver.stop(machine.id)
|
15
|
+
end
|
16
|
+
@app.call(env)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module DockerProvider
|
3
|
+
class Config < Vagrant.plugin("2", :config)
|
4
|
+
attr_accessor :image, :cmd, :ports
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@image = UNSET_VALUE
|
8
|
+
@cmd = UNSET_VALUE
|
9
|
+
@ports = UNSET_VALUE
|
10
|
+
end
|
11
|
+
|
12
|
+
def finalize!
|
13
|
+
@ports = [] if @ports == UNSET_VALUE
|
14
|
+
@cmd = [] if @cmd == UNSET_VALUE
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(machine)
|
18
|
+
errors = _detected_errors
|
19
|
+
|
20
|
+
errors << I18n.t("docker_provider.errors.config.image_not_set") if @image == UNSET_VALUE
|
21
|
+
# TODO: Detect if base image has a CMD / ENTRYPOINT set before erroring out
|
22
|
+
errors << I18n.t("docker_provider.errors.config.cmd_not_set") if @cmd == UNSET_VALUE
|
23
|
+
|
24
|
+
{ "docker-provider" => errors }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "vagrant/util/busy"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
require "vagrant/util/retryable"
|
4
|
+
|
5
|
+
require 'log4r'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module DockerProvider
|
10
|
+
class Driver
|
11
|
+
include Vagrant::Util::Retryable
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@logger = Log4r::Logger.new("vagrant::docker::driver")
|
15
|
+
end
|
16
|
+
|
17
|
+
def create(params)
|
18
|
+
image = params.fetch(:image)
|
19
|
+
ports = Array(params[:ports])
|
20
|
+
volumes = Array(params[:volumes])
|
21
|
+
name = params.fetch(:name)
|
22
|
+
cmd = Array(params.fetch(:cmd))
|
23
|
+
|
24
|
+
run_cmd = %W(docker run -name #{name} -d)
|
25
|
+
run_cmd += ports.map { |p| ['-p', p.to_s] }
|
26
|
+
run_cmd += volumes.map { |v| ['-v', v.to_s] }
|
27
|
+
run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
|
28
|
+
run_cmd += [image, cmd]
|
29
|
+
|
30
|
+
retryable(tries: 10, sleep: 1) do
|
31
|
+
execute(*run_cmd.flatten).chomp
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def state(cid)
|
36
|
+
case
|
37
|
+
when running?(cid)
|
38
|
+
:running
|
39
|
+
when created?(cid)
|
40
|
+
:stopped
|
41
|
+
else
|
42
|
+
:not_created
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def created?(cid)
|
47
|
+
result = execute('docker', 'ps', '-a', '-q').to_s
|
48
|
+
result =~ /^#{Regexp.escape cid}$/
|
49
|
+
end
|
50
|
+
|
51
|
+
def running?(cid)
|
52
|
+
result = execute('docker', 'ps', '-q')
|
53
|
+
result =~ /^#{Regexp.escape cid}$/m
|
54
|
+
end
|
55
|
+
|
56
|
+
def start(cid)
|
57
|
+
unless running?(cid)
|
58
|
+
execute('docker', 'start', cid)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def stop(cid)
|
63
|
+
if running?(cid)
|
64
|
+
execute('docker', 'stop', cid)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def rm(cid)
|
69
|
+
if created?(cid)
|
70
|
+
execute('docker', 'rm', cid)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def inspect(cid)
|
75
|
+
# DISCUSS: Is there a chance that this will change?
|
76
|
+
@data ||= JSON.parse(execute('docker', 'inspect', cid)).first
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def execute(*cmd, &block)
|
82
|
+
result = raw(*cmd, &block)
|
83
|
+
|
84
|
+
if result.exit_code != 0
|
85
|
+
if @interrupted
|
86
|
+
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
87
|
+
else
|
88
|
+
msg = result.stdout.gsub("\r\n", "\n")
|
89
|
+
msg << result.stderr.gsub("\r\n", "\n")
|
90
|
+
raise "#{cmd.inspect}\n#{msg}" #Errors::ExecuteError, :command => command.inspect
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return the output, making sure to replace any Windows-style
|
95
|
+
# newlines with Unix-style.
|
96
|
+
result.stdout.gsub("\r\n", "\n")
|
97
|
+
end
|
98
|
+
|
99
|
+
def raw(*cmd, &block)
|
100
|
+
int_callback = lambda do
|
101
|
+
@interrupted = true
|
102
|
+
@logger.info("Interrupted.")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Append in the options for subprocess
|
106
|
+
cmd << { :notify => [:stdout, :stderr] }
|
107
|
+
|
108
|
+
Vagrant::Util::Busy.busy(int_callback) do
|
109
|
+
Vagrant::Util::Subprocess.execute(*cmd, &block)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative "version"
|
2
|
+
|
3
|
+
require 'vagrant'
|
4
|
+
|
5
|
+
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../../locales/en.yml')
|
6
|
+
I18n.reload!
|
7
|
+
|
8
|
+
module VagrantPlugins
|
9
|
+
module DockerProvider
|
10
|
+
class Plugin < Vagrant.plugin("2")
|
11
|
+
name "docker-provider"
|
12
|
+
|
13
|
+
provider(:docker, parallel: true) do
|
14
|
+
require_relative 'provider'
|
15
|
+
Provider
|
16
|
+
end
|
17
|
+
|
18
|
+
config(:docker, :provider) do
|
19
|
+
require_relative 'config'
|
20
|
+
Config
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'driver'
|
2
|
+
require_relative 'action'
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module DockerProvider
|
6
|
+
class Provider < Vagrant.plugin("2", :provider)
|
7
|
+
attr_reader :driver
|
8
|
+
|
9
|
+
def initialize(machine)
|
10
|
+
@logger = Log4r::Logger.new("vagrant::provider::docker")
|
11
|
+
@machine = machine
|
12
|
+
@driver = Driver.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# @see Vagrant::Plugin::V2::Provider#action
|
16
|
+
def action(name)
|
17
|
+
action_method = "action_#{name}"
|
18
|
+
return Action.send(action_method) if Action.respond_to?(action_method)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the SSH info for accessing the Container.
|
23
|
+
def ssh_info
|
24
|
+
# If the Container is not created then we cannot possibly SSH into it, so
|
25
|
+
# we return nil.
|
26
|
+
return nil if state == :not_created
|
27
|
+
|
28
|
+
network = @driver.inspect(@machine.id)['NetworkSettings']
|
29
|
+
ip = network['IPAddress']
|
30
|
+
|
31
|
+
# If we were not able to identify the container's IP, we return nil
|
32
|
+
# here and we let Vagrant core deal with it ;)
|
33
|
+
return nil unless ip
|
34
|
+
|
35
|
+
{
|
36
|
+
:host => ip,
|
37
|
+
:port => @machine.config.ssh.guest_port
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def state
|
42
|
+
state_id = nil
|
43
|
+
state_id = :not_created if !@machine.id || !@driver.created?(@machine.id)
|
44
|
+
state_id = @driver.state(@machine.id) if @machine.id && !state_id
|
45
|
+
state_id = :unknown if !state_id
|
46
|
+
|
47
|
+
short = state_id.to_s.gsub("_", " ")
|
48
|
+
long = I18n.t("vagrant.commands.status.#{state_id}")
|
49
|
+
|
50
|
+
Vagrant::MachineState.new(state_id, short, long)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
id = @machine.id ? @machine.id : "new container"
|
55
|
+
"Docker (#{id})"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|