vagrant-lxc 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|