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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.vimrc +1 -1
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +10 -9
  7. data/README.md +43 -29
  8. data/boxes/quantal64/download-ubuntu +21 -20
  9. data/boxes/quantal64/lxc-template +11 -11
  10. data/boxes/quantal64/metadata.json +1 -1
  11. data/development/Vagrantfile +8 -4
  12. data/example/Vagrantfile +3 -15
  13. data/lib/vagrant-lxc.rb +0 -2
  14. data/lib/vagrant-lxc/action.rb +1 -14
  15. data/lib/vagrant-lxc/action/boot.rb +8 -9
  16. data/lib/vagrant-lxc/action/check_created.rb +6 -2
  17. data/lib/vagrant-lxc/action/check_running.rb +6 -2
  18. data/lib/vagrant-lxc/action/compress_rootfs.rb +1 -1
  19. data/lib/vagrant-lxc/action/create.rb +15 -7
  20. data/lib/vagrant-lxc/action/created.rb +6 -2
  21. data/lib/vagrant-lxc/action/destroy.rb +6 -2
  22. data/lib/vagrant-lxc/action/disconnect.rb +5 -1
  23. data/lib/vagrant-lxc/action/forced_halt.rb +3 -3
  24. data/lib/vagrant-lxc/action/forward_ports.rb +2 -2
  25. data/lib/vagrant-lxc/action/handle_box_metadata.rb +38 -27
  26. data/lib/vagrant-lxc/action/is_running.rb +6 -2
  27. data/lib/vagrant-lxc/action/share_folders.rb +8 -8
  28. data/lib/vagrant-lxc/config.rb +20 -10
  29. data/lib/vagrant-lxc/driver.rb +162 -0
  30. data/lib/vagrant-lxc/driver/builder.rb +21 -0
  31. data/lib/vagrant-lxc/{container → driver}/cli.rb +16 -11
  32. data/lib/vagrant-lxc/driver/fetch_ip_from_dnsmasq.rb +41 -0
  33. data/lib/vagrant-lxc/driver/fetch_ip_with_attach.rb +29 -0
  34. data/lib/vagrant-lxc/errors.rb +10 -0
  35. data/lib/vagrant-lxc/plugin.rb +4 -0
  36. data/lib/vagrant-lxc/provider.rb +14 -11
  37. data/lib/vagrant-lxc/version.rb +1 -1
  38. data/spec/fixtures/sample-ip-addr-output +2 -0
  39. data/spec/spec_helper.rb +1 -0
  40. data/spec/unit/action/compress_rootfs_spec.rb +4 -4
  41. data/spec/unit/action/forward_ports_spec.rb +3 -3
  42. data/spec/unit/action/handle_box_metadata_spec.rb +52 -26
  43. data/spec/unit/{container → driver}/cli_spec.rb +17 -19
  44. data/spec/unit/driver_spec.rb +173 -0
  45. data/tasks/boxes.rake +3 -3
  46. metadata +13 -15
  47. data/lib/vagrant-lxc/action/base_action.rb +0 -11
  48. data/lib/vagrant-lxc/action/network.rb +0 -21
  49. data/lib/vagrant-lxc/container.rb +0 -141
  50. data/lib/vagrant-lxc/machine_state.rb +0 -25
  51. data/spec/fixtures/sample-ifconfig-output +0 -18
  52. data/spec/unit/container_spec.rb +0 -147
  53. data/spec/unit/machine_state_spec.rb +0 -39
@@ -1,11 +0,0 @@
1
- module Vagrant
2
- module LXC
3
- module Action
4
- class BaseAction
5
- def initialize(app, env)
6
- @app = app
7
- end
8
- end
9
- end
10
- end
11
- end
@@ -1,21 +0,0 @@
1
- module Vagrant
2
- module LXC
3
- module Action
4
- class Network < BaseAction
5
- def call(env)
6
- # TODO: Validate network configuration prior to anything below
7
- @env = env
8
-
9
- env[:machine].config.vm.networks.each do |type, options|
10
- # We only handle private networks
11
- next if type != :private_network
12
- env[:machine].provider_config.start_opts << "lxc.network.ipv4=#{options[:ip]}/24"
13
- end
14
-
15
- # Continue the middleware chain.
16
- @app.call(env)
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,141 +0,0 @@
1
- require 'securerandom'
2
-
3
- require "vagrant-lxc/errors"
4
- require "vagrant-lxc/container/cli"
5
-
6
- require "vagrant/util/retryable"
7
- require "vagrant/util/subprocess"
8
-
9
- module Vagrant
10
- module LXC
11
- class Container
12
- # Root folder where containers are stored
13
- CONTAINERS_PATH = '/var/lib/lxc'
14
-
15
- # Include this so we can use `Subprocess` more easily.
16
- include Vagrant::Util::Retryable
17
-
18
- # This is raised if the container can't be found when initializing it with
19
- # a name.
20
- class NotFound < StandardError; end
21
-
22
- attr_reader :name
23
-
24
- def initialize(name, cli = CLI.new(name))
25
- @name = name
26
- @cli = cli
27
- @logger = Log4r::Logger.new("vagrant::provider::lxc::container")
28
- end
29
-
30
- def validate!
31
- raise NotFound if @name && ! @cli.list.include?(@name)
32
- end
33
-
34
- def base_path
35
- Pathname.new("#{CONTAINERS_PATH}/#{@name}")
36
- end
37
-
38
- def rootfs_path
39
- Pathname.new(base_path.join('config').read.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1])
40
- end
41
-
42
- def create(base_name, target_rootfs_path, metadata = {})
43
- @logger.debug('Creating container using lxc-create...')
44
-
45
- @name = "#{base_name}-#{SecureRandom.hex(6)}"
46
- public_key = Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s
47
- meta_opts = metadata.fetch('template-opts', {}).merge(
48
- '--auth-key' => public_key,
49
- '--cache' => metadata.fetch('rootfs-cache-path')
50
- )
51
-
52
- @cli.name = @name
53
- @cli.create(metadata.fetch('template-name'), target_rootfs_path, meta_opts)
54
-
55
- @name
56
- end
57
-
58
- def share_folders(folders, config)
59
- folders.each do |folder|
60
- guestpath = rootfs_path.join(folder[:guestpath].gsub(/^\//, ''))
61
- unless guestpath.directory?
62
- begin
63
- system "sudo mkdir -p #{guestpath.to_s}"
64
- rescue Errno::EACCES
65
- raise Vagrant::Errors::SharedFolderCreateFailed,
66
- :path => guestpath.to_s
67
- end
68
- end
69
-
70
- config.start_opts << "lxc.mount.entry=#{folder[:hostpath]} #{guestpath} none bind 0 0"
71
- end
72
- end
73
-
74
- def start(config)
75
- @logger.info('Starting container...')
76
-
77
- opts = config.start_opts.dup
78
- if ENV['LXC_START_LOG_FILE']
79
- extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
80
- end
81
-
82
- @cli.transition_to(:running) { |c| c.start(opts, (extra || nil)) }
83
- end
84
-
85
- def halt
86
- @logger.info('Shutting down container...')
87
-
88
- # TODO: issue an lxc-stop if a timeout gets reached
89
- @cli.transition_to(:stopped) { |c| c.shutdown }
90
- end
91
-
92
- def destroy
93
- @cli.destroy
94
- end
95
-
96
- # TODO: This needs to be reviewed and specs needs to be written
97
- def compress_rootfs
98
- # TODO: Our template should not depend on container's arch
99
- arch = base_path.join('config').read.match(/^lxc\.arch\s+=\s+(.+)$/)[1]
100
- rootfs_dirname = File.dirname rootfs_path
101
- basename = rootfs_path.to_s.gsub(/^#{Regexp.escape rootfs_dirname}\//, '')
102
- # TODO: Pass in tmpdir so we can clean up from outside
103
- target_path = "#{Dir.mktmpdir}/rootfs.tar.gz"
104
-
105
- Dir.chdir base_path do
106
- @logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
107
- system "sudo rm -f rootfs.tar.gz && sudo bsdtar -s /#{basename}/rootfs-#{arch}/ --numeric-owner -czf #{target_path} #{basename}/* 2>/dev/null"
108
-
109
- @logger.info "Changing rootfs tarbal owner"
110
- system "sudo chown #{ENV['USER']}:#{ENV['USER']} #{target_path}"
111
- end
112
-
113
- target_path
114
- end
115
-
116
- def state
117
- if @name
118
- @cli.state
119
- end
120
- end
121
-
122
- def assigned_ip
123
- ip = ''
124
- retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
125
- unless ip = get_container_ip_from_ifconfig
126
- # retry
127
- raise LXC::Errors::ExecuteError, :command => "lxc-attach"
128
- end
129
- end
130
- ip
131
- end
132
-
133
- def get_container_ip_from_ifconfig
134
- output = @cli.attach '/sbin/ifconfig', '-v', 'eth0', namespaces: 'network'
135
- if output =~ /\s+inet addr:([0-9.]+)\s+/
136
- return $1.to_s
137
- end
138
- end
139
- end
140
- end
141
- end
@@ -1,25 +0,0 @@
1
- module Vagrant
2
- module LXC
3
- class MachineState < Vagrant::MachineState
4
- CREATED_STATES = %w( running stopped ).map!(&:to_sym)
5
-
6
- def initialize(state_id)
7
- short = state_id.to_s.gsub("_", " ")
8
- long = I18n.t("vagrant.commands.status.#{state_id}")
9
- super(state_id, short, long)
10
- end
11
-
12
- def created?
13
- CREATED_STATES.include?(self.id)
14
- end
15
-
16
- def off?
17
- self.id == :stopped
18
- end
19
-
20
- def running?
21
- self.id == :running
22
- end
23
- end
24
- end
25
- end
@@ -1,18 +0,0 @@
1
- eth0 Link encap:Ethernet HWaddr 00:16:3e:7c:dd:44
2
- inet addr:10.0.3.109 Bcast:10.0.3.255 Mask:255.255.255.0
3
- inet6 addr: fe80::216:3eff:fe7c:dd44/64 Scope:Link
4
- UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
5
- RX packets:204 errors:0 dropped:0 overruns:0 frame:0
6
- TX packets:203 errors:0 dropped:0 overruns:0 carrier:0
7
- collisions:0 txqueuelen:1000
8
- RX bytes:29663 (29.6 KB) TX bytes:30168 (30.1 KB)
9
-
10
- lo Link encap:Local Loopback
11
- inet addr:127.0.0.1 Mask:255.0.0.0
12
- inet6 addr: ::1/128 Scope:Host
13
- UP LOOPBACK RUNNING MTU:16436 Metric:1
14
- RX packets:73 errors:0 dropped:0 overruns:0 frame:0
15
- TX packets:73 errors:0 dropped:0 overruns:0 carrier:0
16
- collisions:0 txqueuelen:0
17
- RX bytes:34173 (34.1 KB) TX bytes:34173 (34.1 KB)
18
-
@@ -1,147 +0,0 @@
1
- require 'unit_helper'
2
-
3
- require 'vagrant'
4
- require 'vagrant-lxc/container'
5
-
6
- describe Vagrant::LXC::Container do
7
- let(:name) { nil }
8
- subject { described_class.new(name) }
9
-
10
- describe 'container name validation' do
11
- let(:unknown_container) { described_class.new('unknown', cli) }
12
- let(:valid_container) { described_class.new('valid', cli) }
13
- let(:new_container) { described_class.new(nil) }
14
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', list: ['valid']) }
15
-
16
- it 'raises a NotFound error if an unknown container name gets provided' do
17
- expect {
18
- unknown_container.validate!
19
- }.to raise_error(Vagrant::LXC::Container::NotFound)
20
- end
21
-
22
- it 'does not raise a NotFound error if a valid container name gets provided' do
23
- expect {
24
- valid_container.validate!
25
- }.to_not raise_error(Vagrant::LXC::Container::NotFound)
26
- end
27
-
28
- it 'does not raise a NotFound error if nil is provider as name' do
29
- expect {
30
- new_container.validate!
31
- }.to_not raise_error(Vagrant::LXC::Container::NotFound)
32
- end
33
- end
34
-
35
- describe 'creation' do
36
- let(:base_name) { 'container-name' }
37
- let(:suffix) { 'random-suffix' }
38
- let(:template_name) { 'template-name' }
39
- let(:rootfs_cache) { '/path/to/cache' }
40
- let(:target_rootfs) { '/path/to/rootfs' }
41
- let(:public_key_path) { Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s }
42
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', :create => true, :name= => true) }
43
-
44
- subject { described_class.new(name, cli) }
45
-
46
- before do
47
- SecureRandom.stub(hex: suffix)
48
- subject.create base_name, target_rootfs, 'template-name' => template_name, 'rootfs-cache-path' => rootfs_cache, 'template-opts' => { '--foo' => 'bar'}
49
- end
50
-
51
- it 'creates container with the right arguments' do
52
- cli.should have_received(:name=).with("#{base_name}-#{suffix}")
53
- cli.should have_received(:create).with(
54
- template_name,
55
- target_rootfs,
56
- '--auth-key' => public_key_path,
57
- '--cache' => rootfs_cache,
58
- '--foo' => 'bar'
59
- )
60
- end
61
- end
62
-
63
- describe 'destruction' do
64
- let(:name) { 'container-name' }
65
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', destroy: true) }
66
-
67
- subject { described_class.new(name, cli) }
68
-
69
- before { subject.destroy }
70
-
71
- it 'delegates to cli object' do
72
- cli.should have_received(:destroy)
73
- end
74
- end
75
-
76
- describe 'start' do
77
- let(:config) { mock(:config, start_opts: ['a=1', 'b=2']) }
78
- let(:name) { 'container-name' }
79
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', start: true) }
80
-
81
- subject { described_class.new(name, cli) }
82
-
83
- before do
84
- cli.stub(:transition_to).and_yield(cli)
85
- end
86
-
87
- it 'starts container with configured lxc settings' do
88
- cli.should_receive(:start).with(['a=1', 'b=2'], nil)
89
- subject.start(config)
90
- end
91
-
92
- it 'expects a transition to running state to take place' do
93
- cli.should_receive(:transition_to).with(:running)
94
- subject.start(config)
95
- end
96
- end
97
-
98
- describe 'halt' do
99
- let(:name) { 'container-name' }
100
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', shutdown: true) }
101
-
102
- subject { described_class.new(name, cli) }
103
-
104
- before do
105
- cli.stub(:transition_to).and_yield(cli)
106
- end
107
-
108
- it 'delegates to cli shutdown' do
109
- cli.should_receive(:shutdown)
110
- subject.halt
111
- end
112
-
113
- it 'expects a transition to running state to take place' do
114
- cli.should_receive(:transition_to).with(:stopped)
115
- subject.halt
116
- end
117
- end
118
-
119
- describe 'state' do
120
- let(:name) { 'random-container-name' }
121
- let(:cli_state) { :something }
122
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', state: cli_state) }
123
-
124
- subject { described_class.new(name, cli) }
125
-
126
- it 'delegates to cli' do
127
- subject.state.should == cli_state
128
- end
129
- end
130
-
131
- describe 'assigned ip' do
132
- # This ip is set on the sample-ifconfig-output fixture
133
- let(:ip) { "10.0.3.109" }
134
- let(:ifconfig_output) { File.read('spec/fixtures/sample-ifconfig-output') }
135
- let(:name) { 'random-container-name' }
136
- let(:cli) { fire_double('Vagrant::LXC::Container::CLI', :attach => ifconfig_output) }
137
-
138
- subject { described_class.new(name, cli) }
139
-
140
- context 'when ip for eth0 gets returned from lxc-attach call' do
141
- it 'gets parsed from ifconfig output' do
142
- subject.assigned_ip.should == ip
143
- cli.should have_received(:attach).with('/sbin/ifconfig', '-v', 'eth0', namespaces: 'network')
144
- end
145
- end
146
- end
147
- end
@@ -1,39 +0,0 @@
1
- require 'unit_helper'
2
-
3
- require 'vagrant-lxc/machine_state'
4
-
5
- describe Vagrant::LXC::MachineState do
6
- describe 'short description' do
7
- subject { described_class.new(:not_created) }
8
-
9
- it 'is a humanized version of state id' do
10
- subject.short_description.should == 'not created'
11
- end
12
- end
13
-
14
- describe 'long description' do
15
- subject { described_class.new(:short_name) }
16
- before { I18n.stub(t: 'some really long description') }
17
-
18
- it 'is a localized version of the state id' do
19
- subject.long_description.should == 'some really long description'
20
- I18n.should have_received(:t).with('vagrant.commands.status.short_name')
21
- end
22
- end
23
-
24
- context 'when state id is :running' do
25
- subject { described_class.new(:running) }
26
-
27
- it { should be_created }
28
- it { should be_running }
29
- it { should_not be_off }
30
- end
31
-
32
- context 'when state id is :stopped' do
33
- subject { described_class.new(:stopped) }
34
-
35
- it { should be_created }
36
- it { should be_off }
37
- it { should_not be_running }
38
- end
39
- end