Merge pull request #1 from fgrehm/provider-scaffold

Scaffold a basic provider based on VirtualBox provider
This commit is contained in:
Fabio Rehm 2013-02-28 19:35:27 -08:00
commit 09489299b2
29 changed files with 574 additions and 116 deletions

4
.gitignore vendored
View file

@ -17,5 +17,9 @@ tmp
_yardoc
doc/
/gems.tags
.vagrant
/cache
/dummy-ubuntu-cloudimg.box

4
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "vagrant-1.1"]
path = vagrant-1.1
[submodule "vagrant"]
path = vagrant
url = git://github.com/mitchellh/vagrant.git

View file

@ -7,11 +7,16 @@ end
gemspec
gem 'vagrant', github: 'mitchellh/vagrant'
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'

View file

@ -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:
@ -31,15 +30,21 @@ 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)
i18n (0.6.1)
i18n (0.6.2)
json (1.6.8)
listen (0.7.3)
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)
@ -57,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)
@ -67,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!

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -5,5 +5,5 @@ end
source "https://rubygems.org"
gem 'vagrant', github: 'mitchellh/vagrant'
gem 'vagrant', path: '../vagrant'
gem 'vagrant-lxc', path: '../'

View file

@ -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:

View file

@ -0,0 +1 @@
{"provider":"lxc"}

View file

@ -1,7 +1,8 @@
require "vagrant-lxc/version"
require "vagrant-lxc/plugin"
module Vagrant
module LXC
# Your code goes here...
end
end

214
lib/vagrant-lxc/actions.rb Normal file
View file

@ -0,0 +1,214 @@
# 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].provider.container.halt
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
env[:machine].provider.container.create
@app.call env
end
end
class Destroy < BaseAction
def call(env)
env[:machine].id = nil
env[:machine].provider.container.destroy
@app.call env
end
end
class Boot < BaseAction
def call(env)
env[:machine].provider.container.start
@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

View file

@ -0,0 +1,6 @@
module Vagrant
module LXC
class Config < Vagrant.plugin("2", :config)
end
end
end

View file

@ -0,0 +1,52 @@
module Vagrant
module LXC
class Container
CONTAINER_STATE_FILE_PATH = '/tmp/vagrant-lxc-container-state-%<id>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

View file

@ -0,0 +1,25 @@
module Vagrant
module LXC
class MachineState < Vagrant::MachineState
CREATED_STATES = %w( running poweroff ).map!(&:to_sym)
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?
CREATED_STATES.include?(self.id)
end
def off?
self.id == :poweroff
end
def running?
self.id == :running
end
end
end
end

23
lib/vagrant-lxc/plugin.rb Normal file
View file

@ -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

View file

@ -0,0 +1,40 @@
require "vagrant-lxc/actions"
require "vagrant-lxc/container"
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)
attr_reader :container
def initialize(machine)
@logger = Log4r::Logger.new("vagrant::provider::lxc")
@machine = machine
@container = Container.new(@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}"
# TODO: Rename to singular
return LXC::Actions.send(action_method) if LXC::Actions.respond_to?(action_method)
nil
end
def state
LXC::MachineState.new(@container.state)
end
def to_s
id = @machine.id ? @machine.id : "new VM"
"LXC (#{id})"
end
end
end
end

View file

@ -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
@ -75,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 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"
@ -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}"

View file

@ -1,59 +1,28 @@
require 'rubygems'
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 'yaml'
require 'shellwords'
require 'rspec-spies'
`mkdir -p tmp`
module TestHelpers
def provider_up
`cd tmp && ../lib/provider up -o /vagrant/tmp/logger.log`
end
def destroy_container!
`cd tmp && ../lib/provider destroy -o /vagrant/tmp/logger.log`
`rm -f tmp/config.yml`
end
def restore_rinetd_conf!
`sudo cp /vagrant/cache/rinetd.conf /etc/rinetd.conf`
`sudo service rinetd restart`
end
def configure_box_with(opts)
opts = opts.dup
opts.keys.each do |key|
opts[key.to_s] = opts.delete(key)
end
File.open('./tmp/config.yml', 'w') { |f| f.puts YAML::dump(opts) }
end
def provider_ssh(options)
options = options.map { |opt, val| "-#{opt} #{Shellwords.escape val}" }
options = options.join(' ')
`cd tmp && ../lib/provider ssh #{options}`
end
end
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
config.include TestHelpers
# 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'
config.after :all do
destroy_container!
restore_rinetd_conf!
end
end

View file

@ -0,0 +1,39 @@
require 'unit_helper'
require 'vagrant-lxc/machine_state'
describe Vagrant::LXC::MachineState do
describe 'short description' do
subject { described_class.new(:not_created) }
it 'is a humanized version of state id' do
subject.short_description.should == 'not created'
end
end
describe 'long description' do
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'
I18n.should have_received(:t).with('vagrant.commands.status.short_name')
end
end
context 'when state id is :running' do
subject { described_class.new(:running) }
it { should be_created }
it { should be_running }
it { should_not be_off }
end
context 'when state id is :poweroff' do
subject { described_class.new(:poweroff) }
it { should be_created }
it { should be_off }
it { should_not be_running }
end
end

5
spec/unit_helper.rb Normal file
View file

@ -0,0 +1,5 @@
require 'spec_helper'
RSpec.configure do |config|
config.include RSpec::Fire
end

59
spike/spec/spec_helper.rb Normal file
View file

@ -0,0 +1,59 @@
require 'rubygems'
require 'bundler/setup'
Bundler.require
require 'yaml'
require 'shellwords'
`mkdir -p tmp`
module TestHelpers
def provider_up
`cd tmp && ../lib/provider up -o /vagrant/tmp/logger.log`
end
def destroy_container!
`cd tmp && ../lib/provider destroy -o /vagrant/tmp/logger.log`
`rm -f tmp/config.yml`
end
def restore_rinetd_conf!
`sudo cp /vagrant/cache/rinetd.conf /etc/rinetd.conf`
`sudo service rinetd restart`
end
def configure_box_with(opts)
opts = opts.dup
opts.keys.each do |key|
opts[key.to_s] = opts.delete(key)
end
File.open('./tmp/config.yml', 'w') { |f| f.puts YAML::dump(opts) }
end
def provider_ssh(options)
options = options.map { |opt, val| "-#{opt} #{Shellwords.escape val}" }
options = options.join(' ')
`cd tmp && ../lib/provider ssh #{options}`
end
end
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
config.include TestHelpers
# 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'
config.after :all do
destroy_container!
restore_rinetd_conf!
end
end

5
tasks/coverage.rake Normal file
View file

@ -0,0 +1,5 @@
desc 'Run specs with code coverage enabled'
task :coverage do
ENV['COVERAGE'] = 'true'
Rake::Task["spec"].execute
end

View file

@ -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

View file

@ -1,4 +0,0 @@
if ENV['USER'] == 'vagrant'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
end

1
vagrant Submodule

@ -0,0 +1 @@
Subproject commit 68aa9c8acf386cd1926dc432a98ddf21ca4ad0e9