vagrant-lxc 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +9 -9
- data/README.md +51 -27
- data/boxes/common/install-salt +11 -0
- data/boxes/ubuntu/finalize +7 -1
- data/example/Vagrantfile +0 -1
- data/lib/vagrant-lxc/action.rb +13 -11
- data/lib/vagrant-lxc/action/clear_forwarded_ports.rb +1 -1
- data/lib/vagrant-lxc/action/create.rb +1 -1
- data/lib/vagrant-lxc/action/fetch_ip_from_dnsmasq_leases.rb +48 -0
- data/lib/vagrant-lxc/action/fetch_ip_with_lxc_attach.rb +46 -0
- data/lib/vagrant-lxc/action/forward_ports.rb +22 -8
- data/lib/vagrant-lxc/action/remove_temporary_files.rb +1 -1
- data/lib/vagrant-lxc/config.rb +25 -1
- data/lib/vagrant-lxc/driver.rb +42 -14
- data/lib/vagrant-lxc/driver/cli.rb +7 -60
- data/lib/vagrant-lxc/errors.rb +7 -0
- data/lib/vagrant-lxc/provider.rb +26 -3
- data/lib/vagrant-lxc/sudo_wrapper.rb +79 -0
- data/lib/vagrant-lxc/version.rb +1 -1
- data/locales/en.yml +6 -0
- data/spec/unit/action/clear_forwarded_ports_spec.rb +2 -2
- data/spec/unit/action/forward_ports_spec.rb +25 -5
- data/spec/unit/driver/cli_spec.rb +10 -14
- data/spec/unit/driver_spec.rb +17 -36
- data/tasks/boxes.rake +13 -7
- data/tasks/boxes.v2.rake +15 -8
- data/vagrant-lxc.gemspec +1 -0
- metadata +8 -6
- data/lib/vagrant-lxc/driver/builder.rb +0 -21
- data/lib/vagrant-lxc/driver/fetch_ip_from_dnsmasq.rb +0 -41
- data/lib/vagrant-lxc/driver/fetch_ip_with_attach.rb +0 -29
@@ -0,0 +1,46 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
module Action
|
4
|
+
class FetchIpWithLxcAttach
|
5
|
+
# Include this so we can use `Subprocess` more easily.
|
6
|
+
include Vagrant::Util::Retryable
|
7
|
+
|
8
|
+
def initialize(app, env)
|
9
|
+
@app = app
|
10
|
+
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_with_lxc_attach")
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
env[:machine_ip] ||= assigned_ip(env)
|
15
|
+
@app.call(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
def assigned_ip(env)
|
19
|
+
driver = env[:machine].provider.driver
|
20
|
+
version = driver.version.match(/^(\d+\.\d+)\./)[1].to_f
|
21
|
+
unless version >= 0.8
|
22
|
+
@logger.debug "lxc version does not support the --namespaces argument to lxc-attach"
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
|
26
|
+
ip = ''
|
27
|
+
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
|
28
|
+
unless ip = get_container_ip_from_ip_addr(driver)
|
29
|
+
# retry
|
30
|
+
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
ip
|
34
|
+
end
|
35
|
+
|
36
|
+
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
|
37
|
+
def get_container_ip_from_ip_addr(driver)
|
38
|
+
output = driver.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: 'network'
|
39
|
+
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
40
|
+
return $1.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -10,12 +10,13 @@ module Vagrant
|
|
10
10
|
def call(env)
|
11
11
|
@env = env
|
12
12
|
|
13
|
-
# Continue, we need the VM to be booted in order to grab its IP
|
14
|
-
@app.call env
|
15
|
-
|
16
13
|
# Get the ports we're forwarding
|
17
14
|
env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
|
18
15
|
|
16
|
+
if @env[:forwarded_ports].any? and not redir_installed?
|
17
|
+
raise Errors::RedirNotInstalled
|
18
|
+
end
|
19
|
+
|
19
20
|
# Warn if we're port forwarding to any privileged ports
|
20
21
|
env[:forwarded_ports].each do |fp|
|
21
22
|
if fp[:host] <= 1024
|
@@ -24,6 +25,9 @@ module Vagrant
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
28
|
+
# Continue, we need the VM to be booted in order to grab its IP
|
29
|
+
@app.call env
|
30
|
+
|
27
31
|
if @env[:forwarded_ports].any?
|
28
32
|
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
|
29
33
|
forward_ports
|
@@ -31,8 +35,6 @@ module Vagrant
|
|
31
35
|
end
|
32
36
|
|
33
37
|
def forward_ports
|
34
|
-
@container_ip = @env[:machine].provider.driver.assigned_ip
|
35
|
-
|
36
38
|
@env[:forwarded_ports].each do |fp|
|
37
39
|
message_attributes = {
|
38
40
|
# TODO: Add support for multiple adapters
|
@@ -45,7 +47,12 @@ module Vagrant
|
|
45
47
|
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
|
46
48
|
message_attributes))
|
47
49
|
|
48
|
-
redir_pid = redirect_port(
|
50
|
+
redir_pid = redirect_port(
|
51
|
+
fp[:host_ip],
|
52
|
+
fp[:host],
|
53
|
+
fp[:guest_ip] || @env[:machine].provider.ssh_info[:host],
|
54
|
+
fp[:guest]
|
55
|
+
)
|
49
56
|
store_redir_pid(fp[:host], redir_pid)
|
50
57
|
end
|
51
58
|
end
|
@@ -64,8 +71,11 @@ module Vagrant
|
|
64
71
|
mappings.values
|
65
72
|
end
|
66
73
|
|
67
|
-
def redirect_port(
|
68
|
-
|
74
|
+
def redirect_port(host_ip, host_port, guest_ip, guest_port)
|
75
|
+
params = %W( --lport=#{host_port} --caddr=#{guest_ip} --cport=#{guest_port} )
|
76
|
+
params.unshift "--laddr=#{host_ip}" if host_ip
|
77
|
+
params << '--syslog' if ENV['REDIR_LOG']
|
78
|
+
redir_cmd = "redir #{params.join(' ')} 2>/dev/null"
|
69
79
|
|
70
80
|
@logger.debug "Forwarding port with `#{redir_cmd}`"
|
71
81
|
spawn redir_cmd
|
@@ -79,6 +89,10 @@ module Vagrant
|
|
79
89
|
pid_file.write(redir_pid)
|
80
90
|
end
|
81
91
|
end
|
92
|
+
|
93
|
+
def redir_installed?
|
94
|
+
system "which redir > /dev/null"
|
95
|
+
end
|
82
96
|
end
|
83
97
|
end
|
84
98
|
end
|
@@ -14,7 +14,7 @@ module Vagrant
|
|
14
14
|
if env[:machine].state.id == :stopped
|
15
15
|
@logger.debug 'Removing temporary files'
|
16
16
|
tmp_path = env[:machine].provider.driver.rootfs_path.join('tmp')
|
17
|
-
|
17
|
+
env[:machine].provider.sudo_wrapper.run('rm', '-rf', "#{tmp_path}/*")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/lib/vagrant-lxc/config.rb
CHANGED
@@ -6,8 +6,15 @@ module Vagrant
|
|
6
6
|
# @return [Array]
|
7
7
|
attr_reader :customizations
|
8
8
|
|
9
|
+
# A String that points to a file that acts as a wrapper for sudo commands.
|
10
|
+
#
|
11
|
+
# This allows us to have a single entry when whitelisting NOPASSWD commands
|
12
|
+
# on /etc/sudoers
|
13
|
+
attr_accessor :sudo_wrapper
|
14
|
+
|
9
15
|
def initialize
|
10
16
|
@customizations = []
|
17
|
+
@sudo_wrapper = UNSET_VALUE
|
11
18
|
end
|
12
19
|
|
13
20
|
# Customize the container by calling `lxc-start` with the given
|
@@ -25,7 +32,24 @@ module Vagrant
|
|
25
32
|
@customizations << [key, value]
|
26
33
|
end
|
27
34
|
|
28
|
-
|
35
|
+
def finalize!
|
36
|
+
@sudo_wrapper = nil if @sudo_wrapper == UNSET_VALUE
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate(machine)
|
40
|
+
errors = []
|
41
|
+
|
42
|
+
if @sudo_wrapper
|
43
|
+
hostpath = Pathname.new(@sudo_wrapper).expand_path(machine.env.root_path)
|
44
|
+
if ! hostpath.file?
|
45
|
+
errors << I18n.t('vagrant_lxc.sudo_wrapper_not_found', path: hostpath.to_s)
|
46
|
+
elsif ! hostpath.executable?
|
47
|
+
errors << I18n.t('vagrant_lxc.sudo_wrapper_not_executable', path: hostpath.to_s)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
{ "lxc provider" => errors }
|
52
|
+
end
|
29
53
|
end
|
30
54
|
end
|
31
55
|
end
|
data/lib/vagrant-lxc/driver.rb
CHANGED
@@ -11,12 +11,16 @@ module Vagrant
|
|
11
11
|
# a name.
|
12
12
|
class ContainerNotFound < StandardError; end
|
13
13
|
|
14
|
+
# Root folder where container configs are stored
|
15
|
+
CONTAINERS_PATH = '/var/lib/lxc'
|
16
|
+
|
14
17
|
attr_reader :container_name,
|
15
18
|
:customizations
|
16
19
|
|
17
|
-
def initialize(container_name, cli =
|
20
|
+
def initialize(container_name, sudo_wrapper, cli = nil)
|
18
21
|
@container_name = container_name
|
19
|
-
@
|
22
|
+
@sudo_wrapper = sudo_wrapper
|
23
|
+
@cli = cli || CLI.new(sudo_wrapper, container_name)
|
20
24
|
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
|
21
25
|
@customizations = []
|
22
26
|
end
|
@@ -33,6 +37,10 @@ module Vagrant
|
|
33
37
|
Pathname.new(base_path.join('config').read.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1])
|
34
38
|
end
|
35
39
|
|
40
|
+
def mac_address
|
41
|
+
@mac_address ||= base_path.join('config').read.match(/^lxc\.network\.hwaddr\s+=\s+(.+)$/)[1]
|
42
|
+
end
|
43
|
+
|
36
44
|
def create(name, template_path, config_file, template_options = {})
|
37
45
|
@cli.name = @container_name = name
|
38
46
|
|
@@ -48,7 +56,7 @@ module Vagrant
|
|
48
56
|
unless guestpath.directory?
|
49
57
|
begin
|
50
58
|
@logger.debug("Guest path doesn't exist, creating: #{guestpath}")
|
51
|
-
|
59
|
+
@sudo_wrapper.run('mkdir', '-p', guestpath.to_s)
|
52
60
|
rescue Errno::EACCES
|
53
61
|
raise Vagrant::Errors::SharedFolderCreateFailed, :path => guestpath.to_s
|
54
62
|
end
|
@@ -64,9 +72,11 @@ module Vagrant
|
|
64
72
|
if ENV['LXC_START_LOG_FILE']
|
65
73
|
extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
|
66
74
|
end
|
67
|
-
customizations = customizations + @customizations
|
68
75
|
|
69
|
-
|
76
|
+
prune_customizations
|
77
|
+
write_customizations(customizations + @customizations)
|
78
|
+
|
79
|
+
@cli.transition_to(:running) { |c| c.start(extra) }
|
70
80
|
end
|
71
81
|
|
72
82
|
def forced_halt
|
@@ -80,6 +90,14 @@ module Vagrant
|
|
80
90
|
@cli.destroy
|
81
91
|
end
|
82
92
|
|
93
|
+
def attach(*command)
|
94
|
+
@cli.attach(*command)
|
95
|
+
end
|
96
|
+
|
97
|
+
def version
|
98
|
+
@version ||= @cli.version
|
99
|
+
end
|
100
|
+
|
83
101
|
# TODO: This needs to be reviewed and specs needs to be written
|
84
102
|
def compress_rootfs
|
85
103
|
rootfs_dirname = File.dirname rootfs_path
|
@@ -89,10 +107,11 @@ module Vagrant
|
|
89
107
|
|
90
108
|
Dir.chdir base_path do
|
91
109
|
@logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
|
92
|
-
|
110
|
+
@sudo_wrapper.run('rm', '-f', 'rootfs.tar.gz')
|
111
|
+
@sudo_wrapper.run('tar', '--numeric-owner', '-czf', target_path, "#{basename}/*")
|
93
112
|
|
94
113
|
@logger.info "Changing rootfs tarbal owner"
|
95
|
-
|
114
|
+
@sudo_wrapper.run('chown', "#{ENV['USER']}:#{ENV['USER']}", target_path)
|
96
115
|
end
|
97
116
|
|
98
117
|
target_path
|
@@ -104,16 +123,25 @@ module Vagrant
|
|
104
123
|
end
|
105
124
|
end
|
106
125
|
|
107
|
-
def
|
126
|
+
def prune_customizations
|
127
|
+
# Use sed to just strip out the block of code which was inserted by Vagrant
|
128
|
+
@logger.debug 'Prunning vagrant-lxc customizations'
|
129
|
+
@sudo_wrapper.su_c("sed -e '/^# VAGRANT-BEGIN/,/^# VAGRANT-END/ d' -ibak #{base_path.join('config')}")
|
108
130
|
end
|
109
131
|
|
110
132
|
protected
|
111
133
|
|
112
|
-
|
113
|
-
|
134
|
+
def write_customizations(customizations)
|
135
|
+
customizations = customizations.map do |key, value|
|
136
|
+
"lxc.#{key}=#{value}"
|
137
|
+
end
|
138
|
+
customizations.unshift '# VAGRANT-BEGIN'
|
139
|
+
customizations << '# VAGRANT-END'
|
114
140
|
|
115
|
-
|
116
|
-
|
141
|
+
config_file = base_path.join('config').to_s
|
142
|
+
customizations.each do |line|
|
143
|
+
@sudo_wrapper.su_c("echo '#{line}' >> #{config_file}")
|
144
|
+
end
|
117
145
|
end
|
118
146
|
|
119
147
|
def import_template(path)
|
@@ -121,11 +149,11 @@ module Vagrant
|
|
121
149
|
tmp_template_path = templates_path.join("lxc-#{template_name}").to_s
|
122
150
|
|
123
151
|
@logger.debug 'Copying LXC template into place'
|
124
|
-
|
152
|
+
@sudo_wrapper.run('cp', path, tmp_template_path)
|
125
153
|
|
126
154
|
yield template_name
|
127
155
|
ensure
|
128
|
-
|
156
|
+
@sudo_wrapper.run('rm', tmp_template_path)
|
129
157
|
end
|
130
158
|
|
131
159
|
TEMPLATES_PATH_LOOKUP = %w(
|
@@ -17,12 +17,10 @@ module Vagrant
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@name = name
|
25
|
-
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
|
20
|
+
def initialize(sudo_wrapper, name = nil)
|
21
|
+
@sudo_wrapper = sudo_wrapper
|
22
|
+
@name = name
|
23
|
+
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
|
26
24
|
end
|
27
25
|
|
28
26
|
def list
|
@@ -65,10 +63,8 @@ module Vagrant
|
|
65
63
|
run :destroy, '--name', @name
|
66
64
|
end
|
67
65
|
|
68
|
-
def start(
|
69
|
-
|
70
|
-
options += extra_opts if extra_opts
|
71
|
-
run :start, '-d', '--name', @name, *options
|
66
|
+
def start(options = [])
|
67
|
+
run :start, '-d', '--name', @name, *Array(options)
|
72
68
|
end
|
73
69
|
|
74
70
|
def stop
|
@@ -110,56 +106,7 @@ module Vagrant
|
|
110
106
|
private
|
111
107
|
|
112
108
|
def run(command, *args)
|
113
|
-
|
114
|
-
end
|
115
|
-
|
116
|
-
# TODO: Review code below this line, it was pretty much a copy and
|
117
|
-
# paste from VirtualBox base driver and has no tests
|
118
|
-
def execute(*command, &block)
|
119
|
-
# Get the options hash if it exists
|
120
|
-
opts = {}
|
121
|
-
opts = command.pop if command.last.is_a?(Hash)
|
122
|
-
|
123
|
-
tries = 0
|
124
|
-
tries = 3 if opts[:retryable]
|
125
|
-
|
126
|
-
sleep = opts.fetch(:sleep, 1)
|
127
|
-
|
128
|
-
# Variable to store our execution result
|
129
|
-
r = nil
|
130
|
-
|
131
|
-
retryable(:on => LXC::Errors::ExecuteError, :tries => tries, :sleep => sleep) do
|
132
|
-
# Execute the command
|
133
|
-
r = raw(*command, &block)
|
134
|
-
|
135
|
-
# If the command was a failure, then raise an exception that is
|
136
|
-
# nicely handled by Vagrant.
|
137
|
-
if r.exit_code != 0
|
138
|
-
if @interrupted
|
139
|
-
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
140
|
-
else
|
141
|
-
raise LXC::Errors::ExecuteError, :command => command.inspect
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# Return the output, making sure to replace any Windows-style
|
147
|
-
# newlines with Unix-style.
|
148
|
-
r.stdout.gsub("\r\n", "\n")
|
149
|
-
end
|
150
|
-
|
151
|
-
def raw(*command, &block)
|
152
|
-
int_callback = lambda do
|
153
|
-
@interrupted = true
|
154
|
-
@logger.info("Interrupted.")
|
155
|
-
end
|
156
|
-
|
157
|
-
# Append in the options for subprocess
|
158
|
-
command << { :notify => [:stdout, :stderr] }
|
159
|
-
|
160
|
-
Vagrant::Util::Busy.busy(int_callback) do
|
161
|
-
Vagrant::Util::Subprocess.execute(*command, &block)
|
162
|
-
end
|
109
|
+
@sudo_wrapper.run("lxc-#{command}", *args)
|
163
110
|
end
|
164
111
|
end
|
165
112
|
end
|
data/lib/vagrant-lxc/errors.rb
CHANGED
@@ -7,6 +7,10 @@ module Vagrant
|
|
7
7
|
error_key(:lxc_execute_error)
|
8
8
|
end
|
9
9
|
|
10
|
+
class LxcNotInstalled < Vagrant::Errors::VagrantError
|
11
|
+
error_key(:lxc_not_installed)
|
12
|
+
end
|
13
|
+
|
10
14
|
# Box related errors
|
11
15
|
class TemplateFileMissing < Vagrant::Errors::VagrantError
|
12
16
|
error_key(:lxc_template_file_missing)
|
@@ -17,6 +21,9 @@ module Vagrant
|
|
17
21
|
class IncompatibleBox < Vagrant::Errors::VagrantError
|
18
22
|
error_key(:lxc_incompatible_box)
|
19
23
|
end
|
24
|
+
class RedirNotInstalled < Vagrant::Errors::VagrantError
|
25
|
+
error_key(:lxc_redir_not_installed)
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
data/lib/vagrant-lxc/provider.rb
CHANGED
@@ -2,7 +2,7 @@ require "log4r"
|
|
2
2
|
|
3
3
|
require "vagrant-lxc/action"
|
4
4
|
require "vagrant-lxc/driver"
|
5
|
-
require "vagrant-lxc/
|
5
|
+
require "vagrant-lxc/sudo_wrapper"
|
6
6
|
|
7
7
|
module Vagrant
|
8
8
|
module LXC
|
@@ -13,9 +13,24 @@ module Vagrant
|
|
13
13
|
@logger = Log4r::Logger.new("vagrant::provider::lxc")
|
14
14
|
@machine = machine
|
15
15
|
|
16
|
+
ensure_lxc_installed!
|
16
17
|
machine_id_changed
|
17
18
|
end
|
18
19
|
|
20
|
+
def sudo_wrapper
|
21
|
+
@shell ||= begin
|
22
|
+
wrapper = @machine.provider_config.sudo_wrapper
|
23
|
+
wrapper = Pathname(wrapper).expand_path(@machine.env.root_path).to_s if wrapper
|
24
|
+
SudoWrapper.new(wrapper)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def ensure_lxc_installed!
|
29
|
+
unless system("which lxc-version > /dev/null")
|
30
|
+
raise Errors::LxcNotInstalled
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
19
34
|
# If the machine ID changed, then we need to rebuild our underlying
|
20
35
|
# container.
|
21
36
|
def machine_id_changed
|
@@ -23,7 +38,7 @@ module Vagrant
|
|
23
38
|
|
24
39
|
begin
|
25
40
|
@logger.debug("Instantiating the container for: #{id.inspect}")
|
26
|
-
@driver = Driver
|
41
|
+
@driver = Driver.new(id, self.sudo_wrapper)
|
27
42
|
@driver.validate!
|
28
43
|
rescue Driver::ContainerNotFound
|
29
44
|
# The container doesn't exist, so we probably have a stale
|
@@ -50,8 +65,16 @@ module Vagrant
|
|
50
65
|
# we return nil.
|
51
66
|
return nil if state == :not_created
|
52
67
|
|
68
|
+
# Run a custom action called "fetch_ip" which does what it says and puts
|
69
|
+
# the IP found into the `:machine_ip` key in the environment.
|
70
|
+
env = @machine.action("fetch_ip")
|
71
|
+
|
72
|
+
# If we were not able to identify the container's IP, we return nil
|
73
|
+
# here and we let Vagrant core deal with it ;)
|
74
|
+
return nil unless env[:machine_ip]
|
75
|
+
|
53
76
|
{
|
54
|
-
:host =>
|
77
|
+
:host => env[:machine_ip],
|
55
78
|
:port => @machine.config.ssh.guest_port
|
56
79
|
}
|
57
80
|
end
|