vagrant-lxc 0.2.0 → 0.3.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/.gitignore +2 -1
- data/.vimrc +1 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +10 -9
- data/README.md +43 -29
- data/boxes/quantal64/download-ubuntu +21 -20
- data/boxes/quantal64/lxc-template +11 -11
- data/boxes/quantal64/metadata.json +1 -1
- data/development/Vagrantfile +8 -4
- data/example/Vagrantfile +3 -15
- data/lib/vagrant-lxc.rb +0 -2
- data/lib/vagrant-lxc/action.rb +1 -14
- data/lib/vagrant-lxc/action/boot.rb +8 -9
- data/lib/vagrant-lxc/action/check_created.rb +6 -2
- data/lib/vagrant-lxc/action/check_running.rb +6 -2
- data/lib/vagrant-lxc/action/compress_rootfs.rb +1 -1
- data/lib/vagrant-lxc/action/create.rb +15 -7
- data/lib/vagrant-lxc/action/created.rb +6 -2
- data/lib/vagrant-lxc/action/destroy.rb +6 -2
- data/lib/vagrant-lxc/action/disconnect.rb +5 -1
- data/lib/vagrant-lxc/action/forced_halt.rb +3 -3
- data/lib/vagrant-lxc/action/forward_ports.rb +2 -2
- data/lib/vagrant-lxc/action/handle_box_metadata.rb +38 -27
- data/lib/vagrant-lxc/action/is_running.rb +6 -2
- data/lib/vagrant-lxc/action/share_folders.rb +8 -8
- data/lib/vagrant-lxc/config.rb +20 -10
- data/lib/vagrant-lxc/driver.rb +162 -0
- data/lib/vagrant-lxc/driver/builder.rb +21 -0
- data/lib/vagrant-lxc/{container → driver}/cli.rb +16 -11
- data/lib/vagrant-lxc/driver/fetch_ip_from_dnsmasq.rb +41 -0
- data/lib/vagrant-lxc/driver/fetch_ip_with_attach.rb +29 -0
- data/lib/vagrant-lxc/errors.rb +10 -0
- data/lib/vagrant-lxc/plugin.rb +4 -0
- data/lib/vagrant-lxc/provider.rb +14 -11
- data/lib/vagrant-lxc/version.rb +1 -1
- data/spec/fixtures/sample-ip-addr-output +2 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/action/compress_rootfs_spec.rb +4 -4
- data/spec/unit/action/forward_ports_spec.rb +3 -3
- data/spec/unit/action/handle_box_metadata_spec.rb +52 -26
- data/spec/unit/{container → driver}/cli_spec.rb +17 -19
- data/spec/unit/driver_spec.rb +173 -0
- data/tasks/boxes.rake +3 -3
- metadata +13 -15
- data/lib/vagrant-lxc/action/base_action.rb +0 -11
- data/lib/vagrant-lxc/action/network.rb +0 -21
- data/lib/vagrant-lxc/container.rb +0 -141
- data/lib/vagrant-lxc/machine_state.rb +0 -25
- data/spec/fixtures/sample-ifconfig-output +0 -18
- data/spec/unit/container_spec.rb +0 -147
- data/spec/unit/machine_state_spec.rb +0 -39
@@ -0,0 +1,162 @@
|
|
1
|
+
require "vagrant/util/retryable"
|
2
|
+
require "vagrant/util/subprocess"
|
3
|
+
|
4
|
+
require "vagrant-lxc/errors"
|
5
|
+
require "vagrant-lxc/driver/cli"
|
6
|
+
|
7
|
+
module Vagrant
|
8
|
+
module LXC
|
9
|
+
class Driver
|
10
|
+
# This is raised if the container can't be found when initializing it with
|
11
|
+
# a name.
|
12
|
+
class ContainerNotFound < StandardError; end
|
13
|
+
|
14
|
+
attr_reader :container_name,
|
15
|
+
:customizations
|
16
|
+
|
17
|
+
def initialize(container_name, cli = CLI.new(container_name))
|
18
|
+
@container_name = container_name
|
19
|
+
@cli = cli
|
20
|
+
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
|
21
|
+
@customizations = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate!
|
25
|
+
raise ContainerNotFound if @container_name && ! @cli.list.include?(@container_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def base_path
|
29
|
+
Pathname.new("#{CONTAINERS_PATH}/#{@container_name}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def rootfs_path
|
33
|
+
Pathname.new(base_path.join('config').read.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1])
|
34
|
+
end
|
35
|
+
|
36
|
+
def create(name, template_path, template_options = {})
|
37
|
+
@cli.name = @container_name = name
|
38
|
+
|
39
|
+
import_template(template_path) do |template_name|
|
40
|
+
@logger.debug "Creating container..."
|
41
|
+
@cli.create template_name, template_options
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def share_folders(folders)
|
46
|
+
folders.each do |folder|
|
47
|
+
guestpath = rootfs_path.join(folder[:guestpath].gsub(/^\//, ''))
|
48
|
+
unless guestpath.directory?
|
49
|
+
begin
|
50
|
+
@logger.debug("Guest path doesn't exist, creating: #{guestpath}")
|
51
|
+
system "sudo mkdir -p #{guestpath.to_s}"
|
52
|
+
rescue Errno::EACCES
|
53
|
+
raise Vagrant::Errors::SharedFolderCreateFailed, :path => guestpath.to_s
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@customizations << ['mount.entry', "#{folder[:hostpath]} #{guestpath} none bind 0 0"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def start(customizations)
|
62
|
+
@logger.info('Starting container...')
|
63
|
+
|
64
|
+
if ENV['LXC_START_LOG_FILE']
|
65
|
+
extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
|
66
|
+
end
|
67
|
+
customizations = customizations + @customizations
|
68
|
+
|
69
|
+
@cli.transition_to(:running) { |c| c.start(customizations, (extra || nil)) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def halt
|
73
|
+
@logger.info('Shutting down container...')
|
74
|
+
|
75
|
+
# TODO: issue an lxc-stop if a timeout gets reached
|
76
|
+
@cli.transition_to(:stopped) { |c| c.shutdown }
|
77
|
+
end
|
78
|
+
|
79
|
+
def destroy
|
80
|
+
@cli.destroy
|
81
|
+
end
|
82
|
+
|
83
|
+
# TODO: This needs to be reviewed and specs needs to be written
|
84
|
+
def compress_rootfs
|
85
|
+
rootfs_dirname = File.dirname rootfs_path
|
86
|
+
basename = rootfs_path.to_s.gsub(/^#{Regexp.escape rootfs_dirname}\//, '')
|
87
|
+
# TODO: Pass in tmpdir so we can clean up from outside
|
88
|
+
target_path = "#{Dir.mktmpdir}/rootfs.tar.gz"
|
89
|
+
|
90
|
+
Dir.chdir base_path do
|
91
|
+
@logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
|
92
|
+
system "sudo rm -f rootfs.tar.gz && sudo tar --numeric-owner -czf #{target_path} #{basename}/*"
|
93
|
+
|
94
|
+
@logger.info "Changing rootfs tarbal owner"
|
95
|
+
system "sudo chown #{ENV['USER']}:#{ENV['USER']} #{target_path}"
|
96
|
+
end
|
97
|
+
|
98
|
+
target_path
|
99
|
+
end
|
100
|
+
|
101
|
+
def state
|
102
|
+
if @container_name
|
103
|
+
@cli.state
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def assigned_ip
|
108
|
+
ip = ''
|
109
|
+
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
|
110
|
+
unless ip = get_container_ip_from_ip_addr
|
111
|
+
# retry
|
112
|
+
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
ip
|
116
|
+
end
|
117
|
+
|
118
|
+
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
|
119
|
+
def get_container_ip_from_ip_addr
|
120
|
+
output = @cli.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: 'network'
|
121
|
+
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
122
|
+
return $1.to_s
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
# Root folder where container configs are stored
|
129
|
+
CONTAINERS_PATH = '/var/lib/lxc'
|
130
|
+
|
131
|
+
def base_path
|
132
|
+
Pathname.new("#{CONTAINERS_PATH}/#{@container_name}")
|
133
|
+
end
|
134
|
+
|
135
|
+
def import_template(path)
|
136
|
+
template_name = "vagrant-tmp-#{@container_name}"
|
137
|
+
tmp_template_path = templates_path.join("lxc-#{template_name}").to_s
|
138
|
+
|
139
|
+
@logger.debug 'Copying LXC template into place'
|
140
|
+
system(%Q[sudo su root -c "cp #{path} #{tmp_template_path}"])
|
141
|
+
|
142
|
+
yield template_name
|
143
|
+
ensure
|
144
|
+
system(%Q[sudo su root -c "rm #{tmp_template_path}"])
|
145
|
+
end
|
146
|
+
|
147
|
+
TEMPLATES_PATH_LOOKUP = %w(
|
148
|
+
/usr/share/lxc/templates
|
149
|
+
/usr/lib/lxc/templates
|
150
|
+
)
|
151
|
+
def templates_path
|
152
|
+
return @templates_path if @templates_path
|
153
|
+
|
154
|
+
path = TEMPLATES_PATH_LOOKUP.find { |candidate| File.directory?(candidate) }
|
155
|
+
# TODO: Raise an user friendly error
|
156
|
+
raise 'Unable to identify lxc templates path!' unless path
|
157
|
+
|
158
|
+
@templates_path = Pathname(path)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'fetch_ip_with_attach'
|
2
|
+
require_relative 'fetch_ip_from_dnsmasq'
|
3
|
+
|
4
|
+
module Vagrant
|
5
|
+
module LXC
|
6
|
+
class Driver
|
7
|
+
class Builder
|
8
|
+
def self.build(id)
|
9
|
+
version = CLI.new.version.match(/^(\d+\.\d+)\./)[1].to_f
|
10
|
+
Driver.new(id).tap do |driver|
|
11
|
+
mod = version >= 0.8 ?
|
12
|
+
Driver::FetchIpWithAttach :
|
13
|
+
Driver::FetchIpFromDsnmasq
|
14
|
+
|
15
|
+
driver.extend(mod)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -5,7 +5,7 @@ require "vagrant-lxc/errors"
|
|
5
5
|
|
6
6
|
module Vagrant
|
7
7
|
module LXC
|
8
|
-
class
|
8
|
+
class Driver
|
9
9
|
class CLI
|
10
10
|
attr_accessor :name
|
11
11
|
|
@@ -23,6 +23,15 @@ module Vagrant
|
|
23
23
|
run(:ls).split(/\s+/).uniq
|
24
24
|
end
|
25
25
|
|
26
|
+
def version
|
27
|
+
if run(:version) =~ /lxc version:\s+(.+)\s*$/
|
28
|
+
$1.downcase
|
29
|
+
else
|
30
|
+
# TODO: Raise an user friendly error
|
31
|
+
raise 'Unable to parse lxc version!'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
26
35
|
def state
|
27
36
|
if @name && run(:info, '--name', @name) =~ /^state:[^A-Z]+([A-Z]+)$/
|
28
37
|
$1.downcase.to_sym
|
@@ -31,28 +40,24 @@ module Vagrant
|
|
31
40
|
end
|
32
41
|
end
|
33
42
|
|
34
|
-
def create(template,
|
43
|
+
def create(template, template_opts = {})
|
35
44
|
extra = template_opts.to_a.flatten
|
36
45
|
extra.unshift '--' unless extra.empty?
|
37
46
|
|
38
|
-
rootfs_args = target_rootfs_path ?
|
39
|
-
['-B', 'dir', '--dir', target_rootfs_path] :
|
40
|
-
[]
|
41
|
-
|
42
47
|
run :create,
|
43
48
|
'--template', template,
|
44
49
|
'--name', @name,
|
45
|
-
*
|
50
|
+
*extra
|
46
51
|
end
|
47
52
|
|
48
53
|
def destroy
|
49
54
|
run :destroy, '--name', @name
|
50
55
|
end
|
51
56
|
|
52
|
-
def start(
|
53
|
-
|
54
|
-
|
55
|
-
run :start, '-d', '--name', @name, *
|
57
|
+
def start(overrides = [], extra_opts = [])
|
58
|
+
options = overrides.map { |key, value| ["-s", "lxc.#{key}=#{value}"] }.flatten
|
59
|
+
options += extra_opts if extra_opts
|
60
|
+
run :start, '-d', '--name', @name, *options
|
56
61
|
end
|
57
62
|
|
58
63
|
def shutdown
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
class Driver
|
4
|
+
module FetchIpFromDsnmasq
|
5
|
+
def assigned_ip
|
6
|
+
@logger.debug 'Loading ip from dnsmasq leases'
|
7
|
+
ip = nil
|
8
|
+
# TODO: Use Vagrant::Util::Retryable
|
9
|
+
10.times do
|
10
|
+
if dnsmasq_leases =~ /#{Regexp.escape mac_address}\s+([0-9.]+)\s+/
|
11
|
+
ip = $1.to_s
|
12
|
+
break
|
13
|
+
else
|
14
|
+
@logger.debug 'Ip could not be parsed from dnsmasq leases file'
|
15
|
+
sleep 2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
# TODO: Raise an user friendly error
|
19
|
+
raise 'Unable to identify container IP!' unless ip
|
20
|
+
ip
|
21
|
+
end
|
22
|
+
|
23
|
+
def mac_address
|
24
|
+
@mac_address ||= base_path.join('config').read.match(/^lxc\.network\.hwaddr\s+=\s+(.+)$/)[1]
|
25
|
+
end
|
26
|
+
|
27
|
+
LEASES_PATHS = %w(
|
28
|
+
/var/lib/misc/dnsmasq.leases
|
29
|
+
/var/lib/dnsmasq/dnsmasq.leases
|
30
|
+
/var/db/dnsmasq.leases
|
31
|
+
)
|
32
|
+
|
33
|
+
def dnsmasq_leases
|
34
|
+
LEASES_PATHS.map do |path|
|
35
|
+
File.read(path) if File.exists?(path)
|
36
|
+
end.join("\n")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Vagrant
|
2
|
+
module LXC
|
3
|
+
class Driver
|
4
|
+
module FetchIpWithAttach
|
5
|
+
# Include this so we can use `Subprocess` more easily.
|
6
|
+
include Vagrant::Util::Retryable
|
7
|
+
|
8
|
+
def assigned_ip
|
9
|
+
ip = ''
|
10
|
+
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
|
11
|
+
unless ip = get_container_ip_from_ip_addr
|
12
|
+
# retry
|
13
|
+
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
ip
|
17
|
+
end
|
18
|
+
|
19
|
+
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
|
20
|
+
def get_container_ip_from_ip_addr
|
21
|
+
output = @cli.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: 'network'
|
22
|
+
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
|
23
|
+
return $1.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/vagrant-lxc/errors.rb
CHANGED
@@ -1,12 +1,22 @@
|
|
1
|
+
require 'vagrant/errors'
|
2
|
+
|
1
3
|
module Vagrant
|
2
4
|
module LXC
|
3
5
|
module Errors
|
4
6
|
class ExecuteError < Vagrant::Errors::VagrantError
|
5
7
|
error_key(:lxc_execute_error)
|
6
8
|
end
|
9
|
+
|
10
|
+
# Box related errors
|
7
11
|
class TemplateFileMissing < Vagrant::Errors::VagrantError
|
8
12
|
error_key(:lxc_template_file_missing)
|
9
13
|
end
|
14
|
+
class RootFSTarballMissing < Vagrant::Errors::VagrantError
|
15
|
+
error_key(:lxc_invalid_box_version)
|
16
|
+
end
|
17
|
+
class InvalidBoxVersion < Vagrant::Errors::VagrantError
|
18
|
+
error_key(:lxc_invalid_box_version)
|
19
|
+
end
|
10
20
|
end
|
11
21
|
end
|
12
22
|
end
|
data/lib/vagrant-lxc/plugin.rb
CHANGED
data/lib/vagrant-lxc/provider.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require "log4r"
|
2
2
|
|
3
3
|
require "vagrant-lxc/action"
|
4
|
-
require "vagrant-lxc/
|
5
|
-
require "vagrant-lxc/
|
4
|
+
require "vagrant-lxc/driver"
|
5
|
+
require "vagrant-lxc/driver/builder"
|
6
6
|
|
7
7
|
module Vagrant
|
8
8
|
module LXC
|
9
9
|
class Provider < Vagrant.plugin("2", :provider)
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :driver
|
11
11
|
|
12
12
|
def initialize(machine)
|
13
13
|
@logger = Log4r::Logger.new("vagrant::provider::lxc")
|
@@ -23,9 +23,9 @@ module Vagrant
|
|
23
23
|
|
24
24
|
begin
|
25
25
|
@logger.debug("Instantiating the container for: #{id.inspect}")
|
26
|
-
@
|
27
|
-
@
|
28
|
-
rescue
|
26
|
+
@driver = Driver::Builder.build(id)
|
27
|
+
@driver.validate!
|
28
|
+
rescue Driver::ContainerNotFound
|
29
29
|
# The container doesn't exist, so we probably have a stale
|
30
30
|
# ID. Just clear the id out of the machine and reload it.
|
31
31
|
@logger.debug("Container not found! Clearing saved machine ID and reloading.")
|
@@ -40,7 +40,6 @@ module Vagrant
|
|
40
40
|
# exists, otherwise return nil to show that we don't support the
|
41
41
|
# given action.
|
42
42
|
action_method = "action_#{name}"
|
43
|
-
# TODO: Rename to singular
|
44
43
|
return LXC::Action.send(action_method) if LXC::Action.respond_to?(action_method)
|
45
44
|
nil
|
46
45
|
end
|
@@ -52,17 +51,21 @@ module Vagrant
|
|
52
51
|
return nil if state == :not_created
|
53
52
|
|
54
53
|
{
|
55
|
-
:host => @
|
54
|
+
:host => @driver.assigned_ip,
|
56
55
|
:port => @machine.config.ssh.guest_port
|
57
56
|
}
|
58
57
|
end
|
59
58
|
|
60
59
|
def state
|
61
60
|
state_id = nil
|
62
|
-
state_id = :not_created if !@
|
63
|
-
state_id = @
|
61
|
+
state_id = :not_created if !@driver.container_name
|
62
|
+
state_id = @driver.state if !state_id
|
64
63
|
state_id = :unknown if !state_id
|
65
|
-
|
64
|
+
|
65
|
+
short = state_id.to_s.gsub("_", " ")
|
66
|
+
long = I18n.t("vagrant.commands.status.#{state_id}")
|
67
|
+
|
68
|
+
Vagrant::MachineState.new(state_id, short, long)
|
66
69
|
end
|
67
70
|
|
68
71
|
def to_s
|
data/lib/vagrant-lxc/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -6,8 +6,8 @@ describe Vagrant::LXC::Action::CompressRootFS do
|
|
6
6
|
let(:app) { mock(:app, call: true) }
|
7
7
|
let(:env) { {machine: machine, ui: stub(info: true)} }
|
8
8
|
let(:machine) { fire_double('Vagrant::Machine', provider: provider) }
|
9
|
-
let(:provider) { fire_double('Vagrant::LXC::Provider',
|
10
|
-
let(:
|
9
|
+
let(:provider) { fire_double('Vagrant::LXC::Provider', driver: driver) }
|
10
|
+
let(:driver) { fire_double('Vagrant::LXC::Driver', compress_rootfs: compressed_rootfs_path) }
|
11
11
|
let(:compressed_rootfs_path) { '/path/to/rootfs.tar.gz' }
|
12
12
|
|
13
13
|
subject { described_class.new(app, env) }
|
@@ -17,8 +17,8 @@ describe Vagrant::LXC::Action::CompressRootFS do
|
|
17
17
|
subject.call(env)
|
18
18
|
end
|
19
19
|
|
20
|
-
it
|
21
|
-
|
20
|
+
it "asks the driver to compress container's rootfs" do
|
21
|
+
driver.should have_received(:compress_rootfs)
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'sets export.temp_dir on action env' do
|