From 16e5b61f20992e38f42d014d74884d6d998447f9 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Wed, 27 Feb 2013 01:59:30 -0300 Subject: [PATCH 01/11] Switch back to vagrant as submodule in order to ease debugging --- .gitmodules | 4 ++-- Gemfile | 2 +- Gemfile.lock | 17 ++++++++--------- dev/Gemfile | 2 +- dev/Gemfile.lock | 15 +++++++-------- setup-vagrant-dev-box | 8 +++++--- vagrant | 1 + 7 files changed, 25 insertions(+), 24 deletions(-) create mode 160000 vagrant diff --git a/.gitmodules b/.gitmodules index 02032a4..bef6aa9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "vagrant-1.1"] - path = vagrant-1.1 +[submodule "vagrant"] + path = vagrant url = git://github.com/mitchellh/vagrant.git diff --git a/Gemfile b/Gemfile index f9b870d..98545dc 100644 --- a/Gemfile +++ b/Gemfile @@ -7,7 +7,7 @@ end gemspec -gem 'vagrant', github: 'mitchellh/vagrant' +gem 'vagrant', path: './vagrant' gem 'rake' gem 'net-ssh' gem 'rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 9bc9f8b..4952507 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,10 @@ -GIT - remote: git://github.com/mitchellh/vagrant.git - revision: 803269f7291719715011c5c76d66e20101f7af50 +PATH + remote: . + specs: + vagrant-lxc (0.0.1) + +PATH + remote: ./vagrant specs: vagrant (1.1.0.dev) childprocess (~> 0.3.7) @@ -11,11 +15,6 @@ GIT net-scp (~> 1.0.4) net-ssh (~> 2.2.2) -PATH - remote: . - specs: - vagrant-lxc (0.0.1) - GEM remote: https://rubygems.org/ specs: @@ -34,7 +33,7 @@ GEM guard-rspec (2.4.1) guard (>= 1.1) rspec (~> 2.11) - i18n (0.6.1) + i18n (0.6.2) json (1.6.8) listen (0.7.3) log4r (1.1.10) diff --git a/dev/Gemfile b/dev/Gemfile index c572628..5ad4b27 100644 --- a/dev/Gemfile +++ b/dev/Gemfile @@ -5,5 +5,5 @@ end source "https://rubygems.org" -gem 'vagrant', github: 'mitchellh/vagrant' +gem 'vagrant', path: '../vagrant' gem 'vagrant-lxc', path: '../' diff --git a/dev/Gemfile.lock b/dev/Gemfile.lock index 5c5c3d0..a7505a6 100644 --- a/dev/Gemfile.lock +++ b/dev/Gemfile.lock @@ -1,6 +1,10 @@ -GIT - remote: git://github.com/mitchellh/vagrant.git - revision: 803269f7291719715011c5c76d66e20101f7af50 +PATH + remote: ../ + specs: + vagrant-lxc (0.0.1) + +PATH + remote: ../vagrant specs: vagrant (1.1.0.dev) childprocess (~> 0.3.7) @@ -11,11 +15,6 @@ GIT net-scp (~> 1.0.4) net-ssh (~> 2.2.2) -PATH - remote: ../ - specs: - vagrant-lxc (0.0.1) - GEM remote: https://rubygems.org/ specs: diff --git a/setup-vagrant-dev-box b/setup-vagrant-dev-box index 8c190c5..8f25665 100755 --- a/setup-vagrant-dev-box +++ b/setup-vagrant-dev-box @@ -58,6 +58,9 @@ end # Cache development dependencies `mkdir -p cache` +# Fetches vagrant submodule +`git submodule update --init` + # Cache container image between vagrant box destructions download "#{IMAGE_ROOT}/#{IMAGE_NAME}", IMAGE_NAME @@ -117,9 +120,8 @@ vagrant_ssh 'sudo chown vagrant:vagrant /etc/rinetd.conf' vagrant_ssh 'cd /vagrant && bundle && cd /vagrant/dev && bundle' # Setup vagrant default ssh key -# FIXME: This is wrong -vagrant_keys_path = '~/gems/bundler/gems/vagrant-803269f72917/keys' -vagrant_ssh "cd /vagrant && mkdir -p ~/.ssh && cp #{vagrant_keys_path}/vagrant ~/.ssh/id_rsa && cp #{vagrant_keys_path}/vagrant.pub ~/.ssh/id_rsa.pub && chmod 600 ~/.ssh/id_rsa" +vagrant_keys_path = '/vagrant/vagrant/keys' +vagrant_ssh "mkdir -p ~/.ssh && cd /vagrant && cp #{vagrant_keys_path}/vagrant ~/.ssh/id_rsa && cp #{vagrant_keys_path}/vagrant.pub ~/.ssh/id_rsa.pub && chmod 600 ~/.ssh/id_rsa" # Setup lxc cache vagrant_ssh "sudo mkdir -p /var/cache/lxc/cloud-quantal && sudo cp /vagrant/cache/#{IMAGE_NAME} /var/cache/lxc/cloud-quantal/#{IMAGE_NAME}" diff --git a/vagrant b/vagrant new file mode 160000 index 0000000..68aa9c8 --- /dev/null +++ b/vagrant @@ -0,0 +1 @@ +Subproject commit 68aa9c8acf386cd1926dc432a98ddf21ca4ad0e9 From 2ceef09646b1bbcc8b6ceb742a6f122877480a49 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Wed, 27 Feb 2013 18:44:41 -0300 Subject: [PATCH 02/11] Install bsdtar package (vagrant dependency) on vagrant dev box --- setup-vagrant-dev-box | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup-vagrant-dev-box b/setup-vagrant-dev-box index 8f25665..0f8f342 100755 --- a/setup-vagrant-dev-box +++ b/setup-vagrant-dev-box @@ -78,7 +78,7 @@ vagrant_ssh 'sudo sed -i -e "s/be.archive/br.archive/g" /etc/apt/sources.list' vagrant_ssh "sudo apt-get update && sudo apt-get upgrade -y" # Install dependencies -vagrant_ssh "sudo apt-get install lxc rinetd libffi-dev libffi-ruby ruby1.9.1-dev htop git virtualbox virtualbox-ose-dkms linux-headers-generic linux-headers-3.5.0-17-generic -y && sudo gem install bundler --no-ri --no-rdoc -v 1.2.5" +vagrant_ssh "sudo apt-get install lxc rinetd libffi-dev bsdtar libffi-ruby ruby1.9.1-dev htop git virtualbox virtualbox-ose-dkms linux-headers-generic linux-headers-3.5.0-17-generic -y && sudo gem install bundler --no-ri --no-rdoc -v 1.2.5" vagrant_ssh "sudo dkms install virtualbox/4.1.18" vagrant_ssh "sudo service virtualbox start" From a5b4817f86a285764b060d6ce1dcc2d304c375de Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Wed, 27 Feb 2013 19:50:34 -0300 Subject: [PATCH 03/11] Move provider spike related code out of the way --- {lib => spike}/provider | 0 {spec => spike}/vagrant_ssh_spec.rb | 0 {spec => spike}/vagrant_up_spec.rb | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {lib => spike}/provider (100%) rename {spec => spike}/vagrant_ssh_spec.rb (100%) rename {spec => spike}/vagrant_up_spec.rb (100%) diff --git a/lib/provider b/spike/provider similarity index 100% rename from lib/provider rename to spike/provider diff --git a/spec/vagrant_ssh_spec.rb b/spike/vagrant_ssh_spec.rb similarity index 100% rename from spec/vagrant_ssh_spec.rb rename to spike/vagrant_ssh_spec.rb diff --git a/spec/vagrant_up_spec.rb b/spike/vagrant_up_spec.rb similarity index 100% rename from spec/vagrant_up_spec.rb rename to spike/vagrant_up_spec.rb From 677c9b17af7a2f2857f3d8aa050884ff3631f387 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Wed, 27 Feb 2013 19:57:19 -0300 Subject: [PATCH 04/11] There is a lot of stuff going on right now, better not worry about keeping docs up to date while things are taking shape --- README.md | 44 ++------------------------------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index ff2b707..91717c6 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,7 @@ # vagrant-lxc -Highly experimental, soon to come, Linux Containers support for the unreleased Vagrant 1.1 - - -## WARNING - -Please keep in mind that this is not even alpha software and things might go wrong. -Although I'm brave enough to use it on my physical machine, its recommended that you -try it out on the Vagrant dev box ;) - - -## Development - -On your host: - -```terminal -./setup-vagrant-dev-box -vagrant ssh -``` - -*NOTE: This takes around 12 minutes on a 15mb connection after the [base vagrant box](https://github.com/fgrehm/vagrant-lxc/blob/master/Vagrantfile#L5-L6) -and ubuntu [lxc cloud img](https://github.com/fgrehm/vagrant-lxc/blob/master/setup-vagrant-dev-box#L8-L9) -have been downloaded* - -On the guest machine: - -```terminal -mkdir /tmp/vagrant-lxc -cp /vagrant/config.yml.sample /tmp/vagrant-lxc/config.yml -cd /tmp/vagrant-lxc -/vagrant/lib/provider up -/vagrant/lib/provider ssh -``` - - -## Troubleshooting - -If your container / dev box start acting weird, run `vagrant reload` to see if -things get back to normal. - -In case `vagrant reload` doesn't work, restore the VirtualBox snapshot that was -created automagically right after `./setup-vagrant-dev-box` finished by running -the same script again and selecting the `[r]estore snapshot` option when asked. +Highly experimental, soon to come, Linux Containers support for the unreleased +Vagrant 1.1. More information coming out soon... ## Contributing From d7fd676adb6433464c2607035e3bf4d0cc6b7ccc Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Wed, 27 Feb 2013 20:13:33 -0300 Subject: [PATCH 05/11] Move spec_helper used on spike out of the way --- {spec => spike/spec}/spec_helper.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {spec => spike/spec}/spec_helper.rb (100%) diff --git a/spec/spec_helper.rb b/spike/spec/spec_helper.rb similarity index 100% rename from spec/spec_helper.rb rename to spike/spec/spec_helper.rb From c54fbebfec544f43365ed2dc8d9b6ad5d0fc7bbd Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Thu, 28 Feb 2013 00:06:29 -0300 Subject: [PATCH 06/11] Guard and rspec boilerplate --- .gitignore | 2 ++ Gemfile | 5 +++++ Gemfile.lock | 19 +++++++++++++++++++ Guardfile | 30 ++++++++++++++++++++++++++++-- Rakefile | 6 +++++- setup-vagrant-dev-box | 2 +- spec/spec_helper.rb | 28 ++++++++++++++++++++++++++++ spec/unit_helper.rb | 5 +++++ tasks/coverage.rake | 5 +++++ tasks/spec.rake | 4 ---- 10 files changed, 98 insertions(+), 8 deletions(-) create mode 100644 spec/spec_helper.rb create mode 100644 spec/unit_helper.rb create mode 100644 tasks/coverage.rake delete mode 100644 tasks/spec.rake diff --git a/.gitignore b/.gitignore index 6d588d6..d3c3b58 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,7 @@ tmp _yardoc doc/ +/gems.tags + .vagrant /cache diff --git a/Gemfile b/Gemfile index 98545dc..b3f0dd4 100644 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,12 @@ gem 'vagrant', path: './vagrant' gem 'rake' gem 'net-ssh' gem 'rspec' +gem 'rspec-fire', require: 'rspec/fire' +gem 'rspec-spies', require: false +gem 'simplecov', require: false gem 'guard' gem 'guard-rspec' +gem 'guard-bundler' +gem 'guard-ctags-bundler' gem 'rb-inotify' gem 'log4r' diff --git a/Gemfile.lock b/Gemfile.lock index 4952507..8f9673d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,6 +30,11 @@ GEM pry (>= 0.9.10) terminal-table (>= 1.4.3) thor (>= 0.14.6) + guard-bundler (1.0.0) + bundler (~> 1.0) + guard (~> 1.1) + guard-ctags-bundler (0.1.6) + guard (>= 1.1) guard-rspec (2.4.1) guard (>= 1.1) rspec (~> 2.11) @@ -39,6 +44,7 @@ GEM log4r (1.1.10) lumberjack (1.0.2) method_source (0.8.1) + multi_json (1.6.1) net-scp (1.0.4) net-ssh (>= 1.99.1) net-ssh (2.2.2) @@ -56,7 +62,15 @@ GEM rspec-core (2.13.0) rspec-expectations (2.13.0) diff-lcs (>= 1.1.3, < 2.0) + rspec-fire (1.1.3) + rspec (~> 2.11) rspec-mocks (2.13.0) + rspec-spies (2.1.3) + rspec (~> 2.0) + simplecov (0.7.1) + multi_json (~> 1.0) + simplecov-html (~> 0.7.1) + simplecov-html (0.7.1) slop (3.4.3) terminal-table (1.4.5) thor (0.17.0) @@ -66,11 +80,16 @@ PLATFORMS DEPENDENCIES guard + guard-bundler + guard-ctags-bundler guard-rspec log4r net-ssh rake rb-inotify rspec + rspec-fire + rspec-spies + simplecov vagrant! vagrant-lxc! diff --git a/Guardfile b/Guardfile index a148482..190fdc0 100644 --- a/Guardfile +++ b/Guardfile @@ -3,8 +3,34 @@ raise 'You should start guard from the dev box!' unless ENV['USER'] == 'vagrant' +guard 'bundler' do + watch('Gemfile') + watch(/^.+\.gemspec/) +end + +guard 'ctags-bundler', :src_path => ["lib"] do + watch(/^(lib|spec\/support)\/.*\.rb$/) + watch('Gemfile.lock') +end + guard 'rspec' do watch(%r{^spec/.+_spec\.rb$}) - watch('spec/spec_helper.rb') { 'spec' } - watch('lib/provider') { 'spec' } + watch(%r{^lib/vagrant-lxc/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" } + watch('spec/unit_helper.rb') { "spec/unit" } + watch('spec/spec_helper.rb') { "spec/" } + + # Rails example + watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" } + watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } + watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] } + watch(%r{^spec/support/(.+)\.rb$}) { "spec" } + watch('config/routes.rb') { "spec/routing" } + watch('app/controllers/application_controller.rb') { "spec/controllers" } + + # Capybara features specs + watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } + + # Turnip features and steps + watch(%r{^spec/acceptance/(.+)\.feature$}) + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' } end diff --git a/Rakefile b/Rakefile index eea5261..8247fb1 100644 --- a/Rakefile +++ b/Rakefile @@ -2,4 +2,8 @@ raise 'This Rakefile is meant to be used from the dev box' unless ENV['USER'] == Dir['./tasks/**/*.rake'].each { |f| load f } -task :default => :spec +begin + require 'rspec/core/rake_task' + RSpec::Core::RakeTask.new(:spec) + task :default => :coverage +rescue LoadError; end diff --git a/setup-vagrant-dev-box b/setup-vagrant-dev-box index 0f8f342..76db20b 100755 --- a/setup-vagrant-dev-box +++ b/setup-vagrant-dev-box @@ -78,7 +78,7 @@ vagrant_ssh 'sudo sed -i -e "s/be.archive/br.archive/g" /etc/apt/sources.list' vagrant_ssh "sudo apt-get update && sudo apt-get upgrade -y" # Install dependencies -vagrant_ssh "sudo apt-get install lxc rinetd libffi-dev bsdtar libffi-ruby ruby1.9.1-dev htop git virtualbox virtualbox-ose-dkms linux-headers-generic linux-headers-3.5.0-17-generic -y && sudo gem install bundler --no-ri --no-rdoc -v 1.2.5" +vagrant_ssh "sudo apt-get install lxc rinetd libffi-dev bsdtar exuberant-ctags libffi-ruby ruby1.9.1-dev htop git virtualbox virtualbox-ose-dkms linux-headers-generic linux-headers-3.5.0-17-generic -y && sudo gem install bundler --no-ri --no-rdoc -v 1.2.5" vagrant_ssh "sudo dkms install virtualbox/4.1.18" vagrant_ssh "sudo service virtualbox start" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..c525fdf --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,28 @@ +if ENV['COVERAGE'] + require 'simplecov' + SimpleCov.start do + # This can probably go away once we stop using vagrant as submodule + add_filter { |source_file| source_file.filename =~ /\/vagrant\/plugins\// } + add_filter { |source_file| source_file.filename =~ /\/vagrant\/lib\/vagrant(\/|\.rb)/ } + end +end + +require 'bundler/setup' + +Bundler.require + +require 'rspec-spies' + +Dir[File.dirname(__FILE__) + "/spec/support/**/*.rb"].each { |f| require f } + +RSpec.configure do |config| + config.treat_symbols_as_metadata_keys_with_true_values = true + config.run_all_when_everything_filtered = true + config.filter_run :focus + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = 'random' +end diff --git a/spec/unit_helper.rb b/spec/unit_helper.rb new file mode 100644 index 0000000..f3b8d6c --- /dev/null +++ b/spec/unit_helper.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +RSpec.configure do |config| + config.include RSpec::Fire +end diff --git a/tasks/coverage.rake b/tasks/coverage.rake new file mode 100644 index 0000000..8dc9835 --- /dev/null +++ b/tasks/coverage.rake @@ -0,0 +1,5 @@ +desc 'Run specs with code coverage enabled' +task :coverage do + ENV['COVERAGE'] = 'true' + Rake::Task["spec"].execute +end diff --git a/tasks/spec.rake b/tasks/spec.rake deleted file mode 100644 index b72e1cf..0000000 --- a/tasks/spec.rake +++ /dev/null @@ -1,4 +0,0 @@ -if ENV['USER'] == 'vagrant' - require 'rspec/core/rake_task' - RSpec::Core::RakeTask.new(:spec) -end From f40d3a1a963f702ff2530d1ae154927cf76bcb4a Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Thu, 28 Feb 2013 00:12:34 -0300 Subject: [PATCH 07/11] Add custom machine state class with some convenience methods (like #created? and #off?) --- lib/vagrant-lxc/machine_state.rb | 63 +++++++++++++++++++++++ spec/unit/machine_state_spec.rb | 87 ++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 lib/vagrant-lxc/machine_state.rb create mode 100644 spec/unit/machine_state_spec.rb diff --git a/lib/vagrant-lxc/machine_state.rb b/lib/vagrant-lxc/machine_state.rb new file mode 100644 index 0000000..3d9bea5 --- /dev/null +++ b/lib/vagrant-lxc/machine_state.rb @@ -0,0 +1,63 @@ +module Vagrant + module LXC + class MachineState < Vagrant::MachineState + CONTAINER_STATE_FILE_PATH = '/tmp/vagrant-lxc-container-state-%s' + CREATED_STATES = %w( running poweroff ).map!(&:to_sym) + + def initialize(machine) + @machine = machine + end + + def id + @id ||= + begin + state_id = nil + state_id = :not_created if !@machine.id + # TODO: Grab the real machine state here + state_id = read_state_from_file if !state_id + state_id = :unknown if !state_id + state_id + end + end + + def short_description + @short ||= self.id.to_s.gsub("_", " ") + end + + def long_description + @long ||= I18n.t("vagrant.commands.status.#{self.id}") + end + + def created? + CREATED_STATES.include?(self.id) + end + + def off? + self.id == :poweroff + end + + def running? + self.id == :running + end + + def update!(state) + return File.delete(state_file_path) if state.to_sym == :not_created + File.open(state_file_path, 'w') { |f| f.print state } + end + + def read_state_from_file + if File.exists?(state_file_path) + File.read(state_file_path).to_sym + elsif @machine.id + :unknown + end + end + private :read_state_from_file + + def state_file_path + @path ||= CONTAINER_STATE_FILE_PATH % {id: @machine.id} + end + private :state_file_path + end + end +end diff --git a/spec/unit/machine_state_spec.rb b/spec/unit/machine_state_spec.rb new file mode 100644 index 0000000..a18bf69 --- /dev/null +++ b/spec/unit/machine_state_spec.rb @@ -0,0 +1,87 @@ +require 'unit_helper' +require 'vagrant-lxc/machine_state' + +describe Vagrant::LXC::MachineState do + let(:machine) { mocked_machine } + let(:state_file_path) { subject.send(:state_file_path) } + + subject { described_class.new(machine) } + + after { File.delete state_file_path if File.exists? state_file_path } + + # Yeah, I know, this test is not really useful, but vagrant will complain + # if the state is not a Vagrant::MachineState: + # https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/machine.rb#L300 + it { should be_a Vagrant::MachineState } + + describe 'state id' do + context 'when machine id is not present' do + let(:machine) { mocked_machine(id: nil) } + + its(:id) { should == :not_created } + end + + context 'when machine id is present' do + let(:machine) { mocked_machine(id: 'machine-id') } + + context 'and state file exists' do + before { File.stub(read: 'running', exists?: true) } + after { File.unstub!(:exists?) } + + it 'reads it from file' do + subject.id.should == :running + end + end + + context 'and state file does not exist' do + it 'returns :unknown' do + subject.id.should == :unknown + end + end + end + end + + describe 'short description' do + before { subject.stub(id: :not_created) } + + it 'is a humanized version of state id' do + subject.short_description.should == 'not created' + end + end + + describe 'long description' do + before do + subject.stub(id: 'short') + I18n.stub(t: 'some really long description') + end + + it 'is a localized version of the state id' do + subject.long_description.should == 'some really long description' + end + + it 'uses the status locale "namespace"' do + I18n.should have_received(:t).with('vagrant.commands.status.short') + end + end + + context 'when state id is :running' do + before { subject.stub(id: :running) } + + it { should be_created } + it { should be_running } + it { should_not be_off } + end + + context 'when state id is :poweroff' do + before { subject.stub(id: :poweroff) } + + it { should be_created } + it { should be_off } + it { should_not be_running } + end + + MACHINE_DEFAULTS = {id: nil} + def mocked_machine(stubbed_methods = {}) + fire_double('Vagrant::Machine', MACHINE_DEFAULTS.merge(stubbed_methods)) + end +end From a579f04bce2d9d78c39892ea73a9b8d856069ed1 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Thu, 28 Feb 2013 00:16:24 -0300 Subject: [PATCH 08/11] Add rake task to package a minimal LXC box --- .gitignore | 2 ++ dummy-box-files/metadata.json | 1 + tasks/package_dummy_box.rake | 4 ++++ 3 files changed, 7 insertions(+) create mode 100644 dummy-box-files/metadata.json create mode 100644 tasks/package_dummy_box.rake diff --git a/.gitignore b/.gitignore index d3c3b58..af66537 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ doc/ .vagrant /cache + +/dummy-ubuntu-cloudimg.box diff --git a/dummy-box-files/metadata.json b/dummy-box-files/metadata.json new file mode 100644 index 0000000..959aa0e --- /dev/null +++ b/dummy-box-files/metadata.json @@ -0,0 +1 @@ +{"provider":"lxc"} diff --git a/tasks/package_dummy_box.rake b/tasks/package_dummy_box.rake new file mode 100644 index 0000000..02bcec4 --- /dev/null +++ b/tasks/package_dummy_box.rake @@ -0,0 +1,4 @@ +desc 'Packages a dummy Vagrant box to be used during development' +task :package_dummy_box do + sh 'cd dummy-box-files/ && tar -czf ../dummy-ubuntu-cloudimg.box ./*' +end From faa2aaa6ab5642105075e531761ab48345f2fc07 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Thu, 28 Feb 2013 00:20:54 -0300 Subject: [PATCH 09/11] Scaffold config, plugin and provider classes --- lib/vagrant-lxc.rb | 3 ++- lib/vagrant-lxc/config.rb | 6 ++++++ lib/vagrant-lxc/plugin.rb | 23 +++++++++++++++++++++++ lib/vagrant-lxc/provider.rb | 24 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 lib/vagrant-lxc/config.rb create mode 100644 lib/vagrant-lxc/plugin.rb create mode 100644 lib/vagrant-lxc/provider.rb diff --git a/lib/vagrant-lxc.rb b/lib/vagrant-lxc.rb index e5bc235..405c1ce 100644 --- a/lib/vagrant-lxc.rb +++ b/lib/vagrant-lxc.rb @@ -1,7 +1,8 @@ require "vagrant-lxc/version" +require "vagrant-lxc/plugin" + module Vagrant module LXC - # Your code goes here... end end diff --git a/lib/vagrant-lxc/config.rb b/lib/vagrant-lxc/config.rb new file mode 100644 index 0000000..732f9e2 --- /dev/null +++ b/lib/vagrant-lxc/config.rb @@ -0,0 +1,6 @@ +module Vagrant + module LXC + class Config < Vagrant.plugin("2", :config) + end + end +end diff --git a/lib/vagrant-lxc/plugin.rb b/lib/vagrant-lxc/plugin.rb new file mode 100644 index 0000000..178450f --- /dev/null +++ b/lib/vagrant-lxc/plugin.rb @@ -0,0 +1,23 @@ +require "vagrant" + +module Vagrant + module LXC + class Plugin < Vagrant.plugin("2") + name "Linux Containers (LXC) provider" + description <<-EOF + The LXC provider allows Vagrant to manage and control + LXC-based virtual machines. + EOF + + provider(:lxc) do + require File.expand_path("../provider", __FILE__) + Provider + end + + config(:lxc, :provider) do + require File.expand_path("../config", __FILE__) + Config + end + end + end +end diff --git a/lib/vagrant-lxc/provider.rb b/lib/vagrant-lxc/provider.rb new file mode 100644 index 0000000..ed0b9b0 --- /dev/null +++ b/lib/vagrant-lxc/provider.rb @@ -0,0 +1,24 @@ +require "vagrant-lxc/machine_state" + +require "log4r" + +module Vagrant + module LXC + # DISCUSS: VirtualBox provider has a #machine_id_changed, do we need to handle it as well? + class Provider < Vagrant.plugin("2", :provider) + def initialize(machine) + @logger = Log4r::Logger.new("vagrant::provider::lxc") + @machine = machine + end + + def state + LXC::MachineState.new(@machine) + end + + def to_s + id = @machine.id ? @machine.id : "new VM" + "LXC (#{id})" + end + end + end +end From 1bdd3f87a6e3fa0255cf1a8b9fbc20f39f5b5b7d Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Thu, 28 Feb 2013 00:21:59 -0300 Subject: [PATCH 10/11] Hook up provider with some "placeholder" actions --- lib/vagrant-lxc/actions.rb | 215 ++++++++++++++++++++++++++++++++++++ lib/vagrant-lxc/provider.rb | 11 ++ 2 files changed, 226 insertions(+) create mode 100644 lib/vagrant-lxc/actions.rb diff --git a/lib/vagrant-lxc/actions.rb b/lib/vagrant-lxc/actions.rb new file mode 100644 index 0000000..5abc959 --- /dev/null +++ b/lib/vagrant-lxc/actions.rb @@ -0,0 +1,215 @@ +# TODO: Split action classes into their own files + +module Vagrant + module LXC + module Actions + # This action is responsible for reloading the machine, which + # brings it down, sucks in new configuration, and brings the + # machine back up with the new configuration. + def self.action_reload + Vagrant::Action::Builder.new.tap do |b| + b.use CheckLXC + b.use Vagrant::Action::Builtin::Call, Created do |env1, b2| + if !env1[:result] + b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated + next + end + + b2.use Vagrant::Action::Builtin::ConfigValidate + b2.use action_halt + b2.use action_start + end + end + end + + # We could do this here as VirtualBox does, but at least for now its better + # to be explicit and have the full constant name in order to easily spot + # what we implemented and what is builtin on Vagrant. + # + # include Vagrant::Action::Builtin + + # This action boots the VM, assuming the VM is in a state that requires + # a bootup (i.e. not saved). + def self.action_boot + Vagrant::Action::Builder.new.tap do |b| + b.use ClearForwardedPorts + b.use Vagrant::Action::Builtin::Provision + b.use Vagrant::Action::Builtin::EnvSet, :port_collision_repair => true + b.use PrepareForwardedPortCollisionParams + b.use ClearSharedFolders + b.use ShareFolders + b.use Network + b.use ForwardPorts + b.use HostName + b.use SaneDefaults + b.use Customize + b.use Boot + end + end + + # This action starts a container, assuming it is already created and exists. + # A precondition of this action is that the container exists. + def self.action_start + Vagrant::Action::Builder.new.tap do |b| + b.use CheckLXC + b.use Vagrant::Action::Builtin::ConfigValidate + b.use Vagrant::Action::Builtin::Call, IsRunning do |env, b2| + # If the VM is running, then our work here is done, exit + next if env[:result] + # TODO: Check if has been saved / frozen and resume + b2.use action_boot + end + end + end + + # This action brings the machine up from nothing, including creating the + # container, configuring metadata, and booting. + def self.action_up + Vagrant::Action::Builder.new.tap do |b| + b.use CheckLXC + b.use Vagrant::Action::Builtin::ConfigValidate + b.use Vagrant::Action::Builtin::Call, Created do |env, b2| + # If the VM is NOT created yet, then do the setup steps + if !env[:result] + b2.use Create + # We'll probably have other actions down here... + end + end + b.use action_start + end + end + + # This is the action that is primarily responsible for halting + # the virtual machine, gracefully or by force. + def self.action_halt + Vagrant::Action::Builder.new.tap do |b| + b.use CheckLXC + b.use Vagrant::Action::Builtin::Call, Created do |env, b2| + if env[:result] + # TODO: If is paused, should resume and then halt + # TODO: If could not gracefully halt, force it + # TODO: b2.use Vagrant::Action::Builtin::GracefulHalt, :poweroff, :running + unless env[:machine].state.off? + puts 'TODO: Halt container using Vagrant::Action::Builtin::GracefulHalt' + env[:machine].state.update!(:poweroff) + end + else + b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated + end + end + end + end + + # This is the action that is primarily responsible for completely + # freeing the resources of the underlying virtual machine. + def self.action_destroy + Vagrant::Action::Builder.new.tap do |b| + b.use CheckLXC + b.use Vagrant::Action::Builtin::Call, Created do |env1, b2| + if !env1[:result] + b2.use MessageNotCreated + next + end + + # TODO: Implement our own DestroyConfirm or propose a builtin action for Vagrant core + b2.use Vagrant::Action::Builtin::Call, VagrantPlugins::ProviderVirtualBox::Action::DestroyConfirm do |env2, b3| + if env2[:result] + b3.use Vagrant::Action::Builtin::ConfigValidate + b3.use Vagrant::Action::Builtin::EnvSet, :force_halt => true + b3.use action_halt + b3.use Destroy + # TODO: VirtualBox provider has a CleanMachineFolder action, do we need something similar? + # TODO: VirtualBox provider has a DestroyUnusedNetworkInterfaces action, do we need something similar? + else + # TODO: Implement our own DestroyConfirm or propose a builtin action for Vagrant core + b3.use VagrantPlugins::ProviderVirtualBox::Action::MessageWillNotDestroy + end + end + end + end + end + + + class BaseAction + def initialize(app, env) + @app = app + end + + def call(env) + puts "TODO: Implement #{self.class.name}" + @app.call(env) + end + end + + class Created < BaseAction + def call(env) + # Set the result to be true if the machine is created. + env[:result] = env[:machine].state.created? + + # Call the next if we have one (but we shouldn't, since this + # middleware is built to run with the Call-type middlewares) + @app.call(env) + end + end + + class IsRunning < BaseAction + def call(env) + # Set the result to be true if the machine is created. + env[:result] = env[:machine].state.running? + + # Call the next if we have one (but we shouldn't, since this + # middleware is built to run with the Call-type middlewares) + @app.call(env) + end + end + + class Create < BaseAction + def call(env) + puts "TODO: Create container" + env[:machine].id = 'TODO-set-a-proper-machine-id' unless env[:machine].id + @app.call env + end + end + + class Destroy < BaseAction + def call(env) + puts "TODO: Destroy container" + env[:machine].id = nil + env[:machine].state.update!(:not_created) + @app.call env + end + end + + class Boot < BaseAction + def call(env) + puts 'TODO: Start container' + env[:machine].state.update!(:running) + @app.call env + end + end + + # TODO: Check if our requirements are met. + class CheckLXC < BaseAction; end + + # TODO: Implement folder sharing with "mount" + class ShareFolders < BaseAction; end + + # TODO: Sets up all networking for the container instance. This includes + # host only networks, bridged networking, forwarded ports, etc. + class Network < BaseAction; end + + # TODO: Implement port forwarding with rinetd + class ForwardPorts < BaseAction; end + + # TODO: Find out which defaults are sane for LXC ;) + class SaneDefaults < BaseAction; end + + # TODO: Find out if the actions below will be needed + class ClearForwardedPorts < BaseAction; end + class PrepareForwardedPortCollisionParams < BaseAction; end + class ClearSharedFolders < BaseAction; end + class HostName < BaseAction; end + class Customize < BaseAction; end + end + end +end diff --git a/lib/vagrant-lxc/provider.rb b/lib/vagrant-lxc/provider.rb index ed0b9b0..9de219b 100644 --- a/lib/vagrant-lxc/provider.rb +++ b/lib/vagrant-lxc/provider.rb @@ -1,3 +1,4 @@ +require "vagrant-lxc/actions" require "vagrant-lxc/machine_state" require "log4r" @@ -11,6 +12,16 @@ module Vagrant @machine = machine end + # @see Vagrant::Plugin::V1::Provider#action + def action(name) + # Attempt to get the action method from the Action class if it + # exists, otherwise return nil to show that we don't support the + # given action. + action_method = "action_#{name}" + return LXC::Actions.send(action_method) if LXC::Actions.respond_to?(action_method) + nil + end + def state LXC::MachineState.new(@machine) end From 7168da249ecb8929b117bd1975800bc0f2f22bc3 Mon Sep 17 00:00:00 2001 From: Fabio Rehm Date: Fri, 1 Mar 2013 00:34:51 -0300 Subject: [PATCH 11/11] Extract container object --- lib/vagrant-lxc/actions.rb | 9 +++-- lib/vagrant-lxc/container.rb | 52 +++++++++++++++++++++++++++ lib/vagrant-lxc/machine_state.rb | 48 +++---------------------- lib/vagrant-lxc/provider.rb | 11 ++++-- spec/unit/machine_state_spec.rb | 62 ++++---------------------------- 5 files changed, 76 insertions(+), 106 deletions(-) create mode 100644 lib/vagrant-lxc/container.rb diff --git a/lib/vagrant-lxc/actions.rb b/lib/vagrant-lxc/actions.rb index 5abc959..17d1a2f 100644 --- a/lib/vagrant-lxc/actions.rb +++ b/lib/vagrant-lxc/actions.rb @@ -91,7 +91,7 @@ module Vagrant # TODO: b2.use Vagrant::Action::Builtin::GracefulHalt, :poweroff, :running unless env[:machine].state.off? puts 'TODO: Halt container using Vagrant::Action::Builtin::GracefulHalt' - env[:machine].state.update!(:poweroff) + env[:machine].provider.container.halt end else b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated @@ -167,23 +167,22 @@ module Vagrant def call(env) puts "TODO: Create container" env[:machine].id = 'TODO-set-a-proper-machine-id' unless env[:machine].id + env[:machine].provider.container.create @app.call env end end class Destroy < BaseAction def call(env) - puts "TODO: Destroy container" env[:machine].id = nil - env[:machine].state.update!(:not_created) + env[:machine].provider.container.destroy @app.call env end end class Boot < BaseAction def call(env) - puts 'TODO: Start container' - env[:machine].state.update!(:running) + env[:machine].provider.container.start @app.call env end end diff --git a/lib/vagrant-lxc/container.rb b/lib/vagrant-lxc/container.rb new file mode 100644 index 0000000..3c12316 --- /dev/null +++ b/lib/vagrant-lxc/container.rb @@ -0,0 +1,52 @@ +module Vagrant + module LXC + class Container + CONTAINER_STATE_FILE_PATH = '/tmp/vagrant-lxc-container-state-%s' + + def initialize(machine) + @machine = machine + end + + def create + puts 'TODO: Create container' + end + + def start + puts 'TODO: Start container' + update!(:running) + end + + def halt + update!(:poweroff) + end + + def destroy + puts "TODO: Destroy container" + File.delete(state_file_path) if state_file_path + end + + def state + # TODO: Grab the real machine state here + read_state_from_file + end + + private + + def update!(state) + File.open(state_file_path, 'w') { |f| f.print state } + end + + def read_state_from_file + if File.exists?(state_file_path) + File.read(state_file_path).to_sym + elsif @machine.id + :unknown + end + end + + def state_file_path + CONTAINER_STATE_FILE_PATH % {id: @machine.id} + end + end + end +end diff --git a/lib/vagrant-lxc/machine_state.rb b/lib/vagrant-lxc/machine_state.rb index 3d9bea5..a3bbc0d 100644 --- a/lib/vagrant-lxc/machine_state.rb +++ b/lib/vagrant-lxc/machine_state.rb @@ -1,31 +1,12 @@ module Vagrant module LXC class MachineState < Vagrant::MachineState - CONTAINER_STATE_FILE_PATH = '/tmp/vagrant-lxc-container-state-%s' - CREATED_STATES = %w( running poweroff ).map!(&:to_sym) + CREATED_STATES = %w( running poweroff ).map!(&:to_sym) - def initialize(machine) - @machine = machine - end - - def id - @id ||= - begin - state_id = nil - state_id = :not_created if !@machine.id - # TODO: Grab the real machine state here - state_id = read_state_from_file if !state_id - state_id = :unknown if !state_id - state_id - end - end - - def short_description - @short ||= self.id.to_s.gsub("_", " ") - end - - def long_description - @long ||= I18n.t("vagrant.commands.status.#{self.id}") + def initialize(state_id) + short = state_id.to_s.gsub("_", " ") + long = I18n.t("vagrant.commands.status.#{state_id}") + super(state_id, short, long) end def created? @@ -39,25 +20,6 @@ module Vagrant def running? self.id == :running end - - def update!(state) - return File.delete(state_file_path) if state.to_sym == :not_created - File.open(state_file_path, 'w') { |f| f.print state } - end - - def read_state_from_file - if File.exists?(state_file_path) - File.read(state_file_path).to_sym - elsif @machine.id - :unknown - end - end - private :read_state_from_file - - def state_file_path - @path ||= CONTAINER_STATE_FILE_PATH % {id: @machine.id} - end - private :state_file_path end end end diff --git a/lib/vagrant-lxc/provider.rb b/lib/vagrant-lxc/provider.rb index 9de219b..9f49caf 100644 --- a/lib/vagrant-lxc/provider.rb +++ b/lib/vagrant-lxc/provider.rb @@ -1,4 +1,5 @@ require "vagrant-lxc/actions" +require "vagrant-lxc/container" require "vagrant-lxc/machine_state" require "log4r" @@ -7,9 +8,12 @@ module Vagrant module LXC # DISCUSS: VirtualBox provider has a #machine_id_changed, do we need to handle it as well? class Provider < Vagrant.plugin("2", :provider) + attr_reader :container + def initialize(machine) - @logger = Log4r::Logger.new("vagrant::provider::lxc") - @machine = machine + @logger = Log4r::Logger.new("vagrant::provider::lxc") + @machine = machine + @container = Container.new(@machine) end # @see Vagrant::Plugin::V1::Provider#action @@ -18,12 +22,13 @@ module Vagrant # exists, otherwise return nil to show that we don't support the # given action. action_method = "action_#{name}" + # TODO: Rename to singular return LXC::Actions.send(action_method) if LXC::Actions.respond_to?(action_method) nil end def state - LXC::MachineState.new(@machine) + LXC::MachineState.new(@container.state) end def to_s diff --git a/spec/unit/machine_state_spec.rb b/spec/unit/machine_state_spec.rb index a18bf69..9865372 100644 --- a/spec/unit/machine_state_spec.rb +++ b/spec/unit/machine_state_spec.rb @@ -1,48 +1,10 @@ require 'unit_helper' + require 'vagrant-lxc/machine_state' describe Vagrant::LXC::MachineState do - let(:machine) { mocked_machine } - let(:state_file_path) { subject.send(:state_file_path) } - - subject { described_class.new(machine) } - - after { File.delete state_file_path if File.exists? state_file_path } - - # Yeah, I know, this test is not really useful, but vagrant will complain - # if the state is not a Vagrant::MachineState: - # https://github.com/mitchellh/vagrant/blob/master/lib/vagrant/machine.rb#L300 - it { should be_a Vagrant::MachineState } - - describe 'state id' do - context 'when machine id is not present' do - let(:machine) { mocked_machine(id: nil) } - - its(:id) { should == :not_created } - end - - context 'when machine id is present' do - let(:machine) { mocked_machine(id: 'machine-id') } - - context 'and state file exists' do - before { File.stub(read: 'running', exists?: true) } - after { File.unstub!(:exists?) } - - it 'reads it from file' do - subject.id.should == :running - end - end - - context 'and state file does not exist' do - it 'returns :unknown' do - subject.id.should == :unknown - end - end - end - end - describe 'short description' do - before { subject.stub(id: :not_created) } + subject { described_class.new(:not_created) } it 'is a humanized version of state id' do subject.short_description.should == 'not created' @@ -50,22 +12,17 @@ describe Vagrant::LXC::MachineState do end describe 'long description' do - before do - subject.stub(id: 'short') - I18n.stub(t: 'some really long description') - end + subject { described_class.new(:short_name) } + before { I18n.stub(t: 'some really long description') } it 'is a localized version of the state id' do subject.long_description.should == 'some really long description' - end - - it 'uses the status locale "namespace"' do - I18n.should have_received(:t).with('vagrant.commands.status.short') + I18n.should have_received(:t).with('vagrant.commands.status.short_name') end end context 'when state id is :running' do - before { subject.stub(id: :running) } + subject { described_class.new(:running) } it { should be_created } it { should be_running } @@ -73,15 +30,10 @@ describe Vagrant::LXC::MachineState do end context 'when state id is :poweroff' do - before { subject.stub(id: :poweroff) } + subject { described_class.new(:poweroff) } it { should be_created } it { should be_off } it { should_not be_running } end - - MACHINE_DEFAULTS = {id: nil} - def mocked_machine(stubbed_methods = {}) - fire_double('Vagrant::Machine', MACHINE_DEFAULTS.merge(stubbed_methods)) - end end