diff --git a/lib/vagrant-lxc/action/handle_box_metadata.rb b/lib/vagrant-lxc/action/handle_box_metadata.rb index c61cbc2..a03348f 100644 --- a/lib/vagrant-lxc/action/handle_box_metadata.rb +++ b/lib/vagrant-lxc/action/handle_box_metadata.rb @@ -4,6 +4,7 @@ module Vagrant # Prepare arguments to be used for lxc-create class HandleBoxMetadata < BaseAction LXC_TEMPLATES_PATH = Pathname.new("/usr/share/lxc/templates") + TEMP_PREFIX = "vagrant-lxc-rootfs-temp-" def initialize(app, env) super @@ -11,25 +12,40 @@ module Vagrant end def call(env) + # We _could_ extract the rootfs to a folder under ~/.vagrant.d/boxes + # but it would open up for a few issues: + # * The rootfs owner is the root user, so we'd need to prepend "sudo" to + # `vagrant box remove` + # * We'd waste a lot of disk space: a compressed Ubuntu rootfs fits 80mb, + # extracted it takes 262mb + # * If something goes wrong during the Container creation process and + # somehow we don't handle, writing to /tmp means that things will get + # flushed on next reboot + rootfs_cache = Dir.mktmpdir(TEMP_PREFIX) box = env[:machine].box - metadata = box.metadata - - metadata.merge!( - 'template-name' => "vagrant-#{box.name}", - 'lxc-cache-path' => box.directory.to_s - ) + template_name = "vagrant-#{box.name}" # Prepends "lxc-" to the template file so that `lxc-create` is able to find it - src = box.directory.join('lxc-template').to_s - dest = LXC_TEMPLATES_PATH.join("lxc-#{metadata['template-name']}").to_s - + lxc_template_src = box.directory.join('lxc-template').to_s + unless File.exists?(lxc_template_src) + raise Errors::TemplateFileMissing.new name: box.name + end + dest = LXC_TEMPLATES_PATH.join("lxc-#{template_name}").to_s @logger.debug('Copying LXC template into place') - # This should only ask for administrative permission once, even - # though its executed in multiple subshells. - system(%Q[sudo su root -c "cp #{src} #{dest}"]) - system(%Q[sudo su root -c "cd #{box.directory} && tar xfz rootfs.tar.gz"]) + system(%Q[sudo su root -c "cp #{lxc_template_src} #{dest}"]) + + @logger.debug('Extracting rootfs') + system(%Q[sudo su root -c "cd #{box.directory} && tar xfz rootfs.tar.gz -C #{rootfs_cache}"]) + + box.metadata.merge!( + 'template-name' => template_name, + 'rootfs-cache-path' => rootfs_cache + ) @app.call(env) + + ensure + system %Q[sudo su root -c "rm -rf #{rootfs_cache}"] end end end diff --git a/lib/vagrant-lxc/container.rb b/lib/vagrant-lxc/container.rb index 7945d13..223459a 100644 --- a/lib/vagrant-lxc/container.rb +++ b/lib/vagrant-lxc/container.rb @@ -33,13 +33,12 @@ module Vagrant def create(metadata = {}) # FIXME: Ruby 1.8 users dont have SecureRandom - # @logger.info('Creating container...') + @logger.debug('Creating container using lxc-create...') + @name = SecureRandom.hex(6) public_key = Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s - meta_opts = metadata.fetch('template-opts', {}).map do |opt, value| - [opt, value] - end.flatten + meta_opts = metadata.fetch('template-opts', {}).to_a.flatten # TODO: Handle errors lxc :create, @@ -49,7 +48,7 @@ module Vagrant '--', # Template options '--auth-key', public_key, - '--cache', metadata['lxc-cache-path'], + '--cache', metadata['rootfs-cache-path'], *meta_opts @name diff --git a/lib/vagrant-lxc/errors.rb b/lib/vagrant-lxc/errors.rb index 8d0057b..f94b831 100644 --- a/lib/vagrant-lxc/errors.rb +++ b/lib/vagrant-lxc/errors.rb @@ -4,6 +4,9 @@ module Vagrant class ExecuteError < Vagrant::Errors::VagrantError error_key(:lxc_execute_error) end + class TemplateFileMissing < Vagrant::Errors::VagrantError + error_key(:lxc_template_file_missing) + end end end end diff --git a/locales/en.yml b/locales/en.yml index 5b9b88e..786b5ef 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -6,3 +6,7 @@ en: For more information on the failure, enable detailed logging by setting the environment variable VAGRANT_LOG to DEBUG. + + lxc_template_file_missing: |- + The template file used for creating the container was not found for %{name} + box. diff --git a/spec/unit/action/handle_box_metadata_spec.rb b/spec/unit/action/handle_box_metadata_spec.rb index 9fabbcc..f661132 100644 --- a/spec/unit/action/handle_box_metadata_spec.rb +++ b/spec/unit/action/handle_box_metadata_spec.rb @@ -5,20 +5,24 @@ require 'vagrant-lxc/action/handle_box_metadata' describe Vagrant::LXC::Action::HandleBoxMetadata do let(:metadata) { {'template-opts' => {'--foo' => 'bar'}} } - let(:box) { mock(:box, name: 'box-name', metadata: metadata, directory: Pathname.new('/path/to/box')) } + let(:box) { mock(:box, name: 'box-name', metadata: metadata, directory: box_directory) } + let(:box_directory) { Pathname.new('/path/to/box') } let(:machine) { mock(:machine, box: box) } let(:app) { mock(:app, call: true) } let(:env) { {machine: machine} } + let(:tmpdir) { '/tmp/rootfs/dir' } subject { described_class.new(app, env) } before do + Dir.stub(mktmpdir: tmpdir) + File.stub(exists?: true) subject.stub(:system) subject.call(env) end - it 'sets box directory as lxc-cache-path' do - metadata['lxc-cache-path'].should == box.directory.to_s + it 'creates a tmp directory to store rootfs-cache-path' do + metadata['rootfs-cache-path'].should == tmpdir end it 'prepends vagrant and box name to template-name' do @@ -26,10 +30,15 @@ describe Vagrant::LXC::Action::HandleBoxMetadata do end it 'copies box template file to the right folder' do - src = box.directory.join('lxc-template').to_s + src = box_directory.join('lxc-template').to_s dest = "/usr/share/lxc/templates/lxc-#{metadata['template-name']}" - subject.should have_received(:system).with("sudo su root -c \"cp #{src} #{dest}\"") + + subject.should have_received(:system). + with("sudo su root -c \"cp #{src} #{dest}\"") end - pending 'extracts rootfs' + it 'extracts rootfs into a tmp folder' do + subject.should have_received(:system). + with(%Q[sudo su root -c "cd #{box_directory} && tar xfz rootfs.tar.gz -C #{tmpdir}"]) + end end diff --git a/spec/unit/container_spec.rb b/spec/unit/container_spec.rb index bf0ab56..0d0d4e8 100644 --- a/spec/unit/container_spec.rb +++ b/spec/unit/container_spec.rb @@ -78,13 +78,13 @@ describe Vagrant::LXC::Container do describe 'creation' do let(:name) { 'random-container-name' } let(:template_name) { 'template-name' } - let(:lxc_cache) { '/path/to/cache' } + let(:rootfs_cache) { '/path/to/cache' } let(:public_key_path) { Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s } before do subject.stub(lxc: true) SecureRandom.stub(hex: name) - subject.create 'template-name' => template_name, 'lxc-cache-path' => lxc_cache, 'template-opts' => { '--foo' => 'bar'} + subject.create 'template-name' => template_name, 'rootfs-cache-path' => rootfs_cache, 'template-opts' => { '--foo' => 'bar'} end it 'calls lxc-create with the right arguments' do @@ -94,7 +94,7 @@ describe Vagrant::LXC::Container do '--name', name, '--', '--auth-key', public_key_path, - '--cache', lxc_cache, + '--cache', rootfs_cache, '--foo', 'bar' ) end