Compare commits

..

No commits in common. "master" and "unprivileged-containers" have entirely different histories.

34 changed files with 894 additions and 860 deletions

1
.gitignore vendored
View file

@ -19,7 +19,6 @@ doc/
/tags
/gems.tags
/Gemfile.lock
.vagrant
/cache

View file

@ -1,10 +1,8 @@
language: ruby
rvm:
- 2.2
- 2.3
- 2.4
- 2.5
install:
- gem install -v 1.12.5 bundler
- bundle _1.12.5_ install --jobs=3 --retry=3
- 2.0.0
- 2.1.1
matrix:
allow_failures:
- rvm: 2.1.1
script: "bundle exec rake ci"

View file

@ -1,116 +1,4 @@
## [1.4.2](https://github.com/fgrehm/vagrant-lxc/compare/v1.4.1...v1.4.2) (Jul 17, 2018)
FIXES:
- Fix problems with `redir` 3.x command line. [[GH-467]]
## [1.4.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.4.0...v1.4.1) (Apr 30, 2018)
FEATURES:
- Add support for LXC v3.0
- Add support for `redir` 3.x command line. [[GH-460]]
[GH-460]: https://github.com/fgrehm/vagrant-lxc/issues/460
## [1.4.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.3.1...v1.4.0) (Mar 04, 2018)
FEATURES:
- Add support for unprivileged containers. [[GH-312]]
[GH-312]: https://github.com/fgrehm/vagrant-lxc/issues/312
## [1.3.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.3.0...v1.3.1) (Fev 06, 2018)
FIXES:
- Fix problems with `tmpfs` fiddling in v1.3.0. [[GH-455]]
[GH-455]: https://github.com/fgrehm/vagrant-lxc/pull/455
## [1.3.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.4...v1.3.0) (Jan 20, 2018)
FEATURES:
- lxc-template: make runnable by unprivileged users [[GH-447]]
- Use `lxc-info` instead of `lxc-attach` to retrieve container IP
- Add support for LXC v2.1+ [[GH-445]]
- Remove 2Gb limitation on `/tmp`. [[GH-406]]
OTHERS:
- Bump Vagrant requirements to v1.8+
- Bump LXC requirements to v1.0+
[GH-447]: https://github.com/fgrehm/vagrant-lxc/pull/447
[GH-445]: https://github.com/fgrehm/vagrant-lxc/pull/445
[GH-406]: https://github.com/fgrehm/vagrant-lxc/pull/406
## [1.2.4](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.3...v1.2.4) (Dec 20, 2017)
BUGFIX:
- Support alternative `lxcpath` [[GH-413]]
- Update `pipework` regexp in sudo wrapper for Vagrant 1.9+ [[GH-438]]
- Work around restrictive `umask` values [[GH-435]]
- Make `--config` in `lxc-template` optional [[GH-421]]
- Fix sudo wrapper binpath construction logic [[GH-410]]
- Fix bug causing CTRL-C on `vagrant up` to destroy the VM [[GH-449]]
[GH-413]: https://github.com/fgrehm/vagrant-lxc/pull/413
[GH-438]: https://github.com/fgrehm/vagrant-lxc/pull/438
[GH-435]: https://github.com/fgrehm/vagrant-lxc/pull/435
[GH-421]: https://github.com/fgrehm/vagrant-lxc/pull/421
[GH-410]: https://github.com/fgrehm/vagrant-lxc/pull/410
[GH-449]: https://github.com/fgrehm/vagrant-lxc/pull/449
## [1.2.3](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.2...v1.2.3) (Dec 20, 2016)
- Fix bug in Gemfile.lock
## [1.2.2](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.1...v1.2.2) (Dec 20, 2016)
BUGFIX:
- Make the timeout for fetching container IP's configurable [[GH-426]]
- Load locale file only once [[GH-423]]
- Preserve xattrs in container filesystems [[GH-411]]
- Forward port latest pipework script [[GH-408]]
- Fix handling of non-fatal lxc-stop return code [[GH-405]]
[GH-426]: https://github.com/fgrehm/vagrant-lxc/pull/426
[GH-423]: https://github.com/fgrehm/vagrant-lxc/pull/423
[GH-411]: https://github.com/fgrehm/vagrant-lxc/pull/411
[GH-408]: https://github.com/fgrehm/vagrant-lxc/pull/408
[GH-405]: https://github.com/fgrehm/vagrant-lxc/pull/405
## [1.2.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.0...v1.2.1) (Sep 24, 2015)
BUGFIX:
- Fix sudo Wrapper [[GH-393]]
[GH-393]: https://github.com/fgrehm/vagrant-lxc/pull/393
## [1.2.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.1.0...v1.2.0) (Sep 15, 2015)
FEATURES:
- Support private networking using DHCP [[GH-352]]
[GH-352]: https://github.com/fgrehm/vagrant-lxc/pull/352
IMPROVEMENTS:
- Move mountpoint creation to lxc template for lvm rootfs support [[GH-361]] / [[GH-359]]
- Mount selinux sys dir read-only [[GH-357]] / [[GH-301]]
- Use correct ruby interpreter when generating sudoers file [[GH-355]]
- Fix shebangs to be more portable [[GH-376]]
- Fix removal of lxcbr0/virbr0 when using private networking [[GH-383]]
- Improve /tmp handling by using tmpfs [[GH-362]]
[GH-301]: https://github.com/fgrehm/vagrant-lxc/issues/301
[GH-355]: https://github.com/fgrehm/vagrant-lxc/pull/355
[GH-357]: https://github.com/fgrehm/vagrant-lxc/pull/357
[GH-359]: https://github.com/fgrehm/vagrant-lxc/issues/359
[GH-361]: https://github.com/fgrehm/vagrant-lxc/pull/361
[GH-376]: https://github.com/fgrehm/vagrant-lxc/pull/376
[GH-383]: https://github.com/fgrehm/vagrant-lxc/pull/383
[GH-362]: https://github.com/fgrehm/vagrant-lxc/pull/362
## [1.1.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.1...v1.1.0) (Jan 14, 2015)
## [1.1.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.1...1.1.0) (Jan 14, 2015)
BACKWARDS INCOMPATIBILITIES:

View file

@ -1,16 +1,16 @@
source 'https://rubygems.org'
group :development do
gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git'
gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git', tag: 'v1.7.2'
gem 'guard'
gem 'guard-rspec'
gem 'rb-inotify'
end
group :development, :test do
gem 'rake', '~> 10.4.2'
gem 'rspec', '~> 3.5.0'
gem 'coveralls', '~> 0.7.2', require: (ENV['COVERAGE'] == 'true')
gem 'rake'
gem 'rspec', '2.99.0'
gem 'coveralls', require: (ENV['COVERAGE'] == 'true')
gem 'vagrant-spec', git: 'https://github.com/mitchellh/vagrant-spec.git'
end

197
Gemfile.lock Normal file
View file

@ -0,0 +1,197 @@
GIT
remote: https://github.com/fgrehm/vagrant-cachier.git
revision: 9f6b615e84364b851939a8e7ee8229fc0d276c73
specs:
vagrant-cachier (1.1.0)
GIT
remote: https://github.com/fgrehm/vagrant-pristine.git
revision: 503dbc47848c81d0fbfa6840491856f518d244a1
specs:
vagrant-pristine (0.3.0)
GIT
remote: https://github.com/mitchellh/vagrant-spec.git
revision: 1df5a3af81cb7cce568b2eac52b8f6822bcb1d8e
specs:
vagrant-spec (0.0.1)
childprocess (~> 0.5.0)
log4r (~> 1.1.9)
rspec (~> 2.14)
thor (~> 0.18.1)
GIT
remote: https://github.com/mitchellh/vagrant.git
revision: 1cd667b243f4a263cd5322b6455165cc676b6f7f
tag: v1.7.2
specs:
vagrant (1.7.2)
bundler (>= 1.5.2, < 1.8.0)
childprocess (~> 0.5.0)
erubis (~> 2.7.0)
hashicorp-checkpoint (~> 0.1.1)
i18n (~> 0.6.0)
listen (~> 2.8.0)
log4r (~> 1.1.9, < 1.1.11)
net-scp (~> 1.1.0)
net-sftp (~> 2.1)
net-ssh (>= 2.6.6, < 2.10.0)
nokogiri (= 1.6.3.1)
rb-kqueue (~> 0.2.0)
rest-client (>= 1.6.0, < 2.0)
wdm (~> 0.1.0)
winrm (~> 1.1.3)
PATH
remote: .
specs:
vagrant-lxc (1.1.0)
GEM
remote: https://rubygems.org/
specs:
akami (1.2.2)
gyoku (>= 0.4.0)
nokogiri
builder (3.2.2)
celluloid (0.16.0)
timers (~> 4.0.0)
childprocess (0.5.5)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.0)
coveralls (0.7.1)
multi_json (~> 1.3)
rest-client
simplecov (>= 0.7)
term-ansicolor
thor
diff-lcs (1.2.5)
docile (1.1.5)
erubis (2.7.0)
ffi (1.9.6)
formatador (0.2.5)
gssapi (1.0.3)
ffi (>= 1.0.1)
guard (2.11.1)
formatador (>= 0.2.4)
listen (~> 2.7)
lumberjack (~> 1.0)
nenv (~> 0.1)
notiffany (~> 0.0)
pry (>= 0.9.12)
shellany (~> 0.0)
thor (>= 0.18.1)
guard-compat (1.2.0)
guard-rspec (4.5.0)
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
gyoku (1.2.2)
builder (>= 2.1.2)
hashicorp-checkpoint (0.1.4)
hitimes (1.2.2)
httpclient (2.6.0.1)
httpi (0.9.7)
rack
i18n (0.6.11)
listen (2.8.5)
celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9)
little-plugger (1.1.3)
log4r (1.1.10)
logging (1.8.2)
little-plugger (>= 1.1.3)
multi_json (>= 1.8.4)
lumberjack (1.0.9)
method_source (0.8.2)
mime-types (2.4.3)
mini_portile (0.6.0)
multi_json (1.10.1)
nenv (0.1.1)
net-scp (1.1.2)
net-ssh (>= 2.6.5)
net-sftp (2.1.2)
net-ssh (>= 2.6.5)
net-ssh (2.9.1)
netrc (0.10.2)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
nori (1.1.5)
notiffany (0.0.2)
nenv (~> 0.1)
shellany (~> 0.0)
pry (0.10.1)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
rack (1.6.0)
rake (10.4.2)
rb-fsevent (0.9.4)
rb-inotify (0.9.5)
ffi (>= 0.5.0)
rb-kqueue (0.2.3)
ffi (>= 0.5.0)
rest-client (1.7.2)
mime-types (>= 1.16, < 3.0)
netrc (~> 0.7)
rspec (2.99.0)
rspec-core (~> 2.99.0)
rspec-expectations (~> 2.99.0)
rspec-mocks (~> 2.99.0)
rspec-core (2.99.2)
rspec-expectations (2.99.2)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.99.2)
rubyntlm (0.1.1)
savon (0.9.5)
akami (~> 1.0)
builder (>= 2.1.2)
gyoku (>= 0.4.0)
httpi (~> 0.9)
nokogiri (>= 1.4.0)
nori (~> 1.0)
wasabi (~> 1.0)
shellany (0.0.1)
simplecov (0.9.1)
docile (~> 1.1.0)
multi_json (~> 1.0)
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
slop (3.6.0)
term-ansicolor (1.3.0)
tins (~> 1.0)
thor (0.18.1)
timers (4.0.1)
hitimes
tins (1.3.3)
uuidtools (2.1.5)
vagrant-omnibus (1.4.1)
wasabi (1.0.0)
nokogiri (>= 1.4.0)
wdm (0.1.0)
winrm (1.1.3)
gssapi (~> 1.0.0)
httpclient (~> 2.2, >= 2.2.0.2)
logging (~> 1.6, >= 1.6.1)
nokogiri (~> 1.5)
rubyntlm (~> 0.1.1)
savon (= 0.9.5)
uuidtools (~> 2.1.2)
PLATFORMS
ruby
DEPENDENCIES
coveralls
guard
guard-rspec
rake
rb-inotify
rspec (= 2.99.0)
vagrant!
vagrant-cachier!
vagrant-lxc!
vagrant-omnibus
vagrant-pristine!
vagrant-spec!

View file

@ -1,34 +1,33 @@
🟢 We plan to support and maintain vagrant-lxc, as well as clean it up.<br/>
🟢 Please feel free to contribute Issues and pull requests.<br/>
🟢 P.S: Thanks [Fabio Rehm](https://fabiorehm.com) for the amazing initial project.
# vagrant-lxc
[![Build Status](https://travis-ci.org/fgrehm/vagrant-lxc.png?branch=master)](https://travis-ci.org/fgrehm/vagrant-lxc) [![Gem Version](https://badge.fury.io/rb/vagrant-lxc.png)](http://badge.fury.io/rb/vagrant-lxc) [![Code Climate](https://codeclimate.com/github/fgrehm/vagrant-lxc.png)](https://codeclimate.com/github/fgrehm/vagrant-lxc) [![Coverage Status](https://coveralls.io/repos/fgrehm/vagrant-lxc/badge.png?branch=master)](https://coveralls.io/r/fgrehm/vagrant-lxc) [![Gitter chat](https://badges.gitter.im/fgrehm/vagrant-lxc.png)](https://gitter.im/fgrehm/vagrant-lxc)
[![Build Status](https://travis-ci.org/fgrehm/vagrant-lxc.png?branch=master)](https://travis-ci.org/fgrehm/vagrant-lxc) [![Gem Version](https://badge.fury.io/rb/vagrant-lxc.png)](http://badge.fury.io/rb/vagrant-lxc) [![Code Climate](https://codeclimate.com/github/fgrehm/vagrant-lxc.png)](https://codeclimate.com/github/fgrehm/vagrant-lxc) [![Coverage Status](https://coveralls.io/repos/fgrehm/vagrant-lxc/badge.png?branch=master)](https://coveralls.io/r/fgrehm/vagrant-lxc) [![Gittip](http://img.shields.io/gittip/fgrehm.svg)](https://www.gittip.com/fgrehm/) [![Gitter chat](https://badges.gitter.im/fgrehm/vagrant-lxc.png)](https://gitter.im/fgrehm/vagrant-lxc)
[LXC](http://lxc.sourceforge.net/) provider for [Vagrant](http://www.vagrantup.com/) 1.9+
[LXC](http://lxc.sourceforge.net/) provider for [Vagrant](http://www.vagrantup.com/) 1.6+
This is a Vagrant plugin that allows it to control and provision Linux Containers
as an alternative to the built in VirtualBox provider for Linux hosts. Check out
[this blog post](http://fabiorehm.com/blog/2013/04/28/lxc-provider-for-vagrant/)
to see it in action.
## Features
* Provides the same workflow as the Vagrant VirtualBox provider
* Port forwarding via [`redir`](https://github.com/troglobit/redir)
* Port forwarding via [`redir`](http://linux.die.net/man/1/redir)
* Private networking via [`pipework`](https://github.com/jpetazzo/pipework)
## Requirements
* [Vagrant 1.9+](http://www.vagrantup.com/downloads.html)
* lxc >=2.1
* [Vagrant 1.5+](http://www.vagrantup.com/downloads.html) (tested with 1.7.2)
* lxc 0.7.5+
* `redir` (if you are planning to use port forwarding)
* `brctl` (if you are planning to use private networks, on Ubuntu this means `apt-get install bridge-utils`)
* A [kernel != 3.5.0-17.28](https://github.com/fgrehm/vagrant-lxc/wiki/Troubleshooting#wiki-im-unable-to-restart-containers)
The plugin is known to work better and pretty much out of the box on Ubuntu 14.04+
hosts and installing the dependencies on it basically means a
`apt-get install lxc lxc-templates cgroup-lite redir`. For setting up other
hosts and installing the dependencies on it basically means a `apt-get install lxc lxc-templates cgroup-lite redir`
(older LXC versions like 0.7.5 shipped with Ubuntu 12.04 by default might require
[additional configurations to work](#backingstore-options)). For setting up other
types of hosts please have a look at the [Wiki](https://github.com/fgrehm/vagrant-lxc/wiki).
If you are on a Mac or Windows machine, you might want to have a look at [this](http://the.taoofmac.com/space/HOWTO/Vagrant)
@ -55,9 +54,8 @@ _More information about skipping the `--provider` argument can be found at the
## Base boxes
Base boxes provided on Atlas haven't been refreshed for a good while and shouldn't be relied on.
Your best best is to build your boxes yourself. Some scripts to build your own are available at
[hsoft/vagrant-lxc-base-boxes](https://github.com/hsoft/vagrant-lxc-base-boxes).
Base boxes can be found on [Atlas](https://atlas.hashicorp.com/boxes/search?provider=lxc)
and some scripts to build your own are available at [fgrehm/vagrant-lxc-base-boxes](https://github.com/fgrehm/vagrant-lxc-base-boxes).
If you want to build your own boxes, please have a look at [`BOXES.md`](https://github.com/fgrehm/vagrant-lxc/tree/master/BOXES.md)
for more information.
@ -83,7 +81,7 @@ prior to starting it.
For other configuration options, please check the [lxc.conf manpages](http://manpages.ubuntu.com/manpages/precise/man5/lxc.conf.5.html).
### Private Networks
### Private Networks [EXPERIMENTAL]
Starting with vagrant-lxc 1.1.0, there is some rudimentary support for configuring
[Private Networks](https://docs.vagrantup.com/v2/networking/private_network.html)
@ -133,7 +131,7 @@ specified from the provider block and it defaults to `best`, to change it:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc|
lxc.backingstore = 'lvm' # or 'btrfs', 'overlayfs', ...
lxc.backingstore = 'lvm' # or 'btrfs',...
# lvm specific options
lxc.backingstore_option '--vgname', 'schroots'
lxc.backingstore_option '--fssize', '5G'
@ -142,33 +140,31 @@ Vagrant.configure("2") do |config|
end
```
## Unprivileged containers support
Since v1.4.0, `vagrant-lxc` gained support for unprivileged containers. For now, since it's a new
feature, privileged containers are still the default, but you can have your `Vagrantfile` use
unprivileged containers with the `privileged` flag (which defaults to `true`). Example:
For old versions of lxc (like 0.7.5 shipped with Ubuntu 12.04 by default) that
does not support `best` for the backingstore option, changing it to `none` is
required and a default for all Vagrant environments can be set from your
`~/.vagrant.d/Vagrantfile` using the same `provider` block:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc|
lxc.privileged = false
lxc.backingstore = 'none'
end
end
```
For unprivileged containers to work with `vagrant-lxc`, you need a properly configured system. On
some distros, it can be somewhat of a challenge. Your journey to configuring your system can start
with [Stéphane Graber's blog post about it](https://stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/).
## Avoiding `sudo` passwords
If you're not using unprivileged containers, this plugin requires **a lot** of `sudo`ing To work
around that, you can use the `vagrant lxc sudoers` command which will create a file under
`/etc/sudoers.d/vagrant-lxc` whitelisting all commands required by `vagrant-lxc` to run.
This plugin requires **a lot** of `sudo`ing since [user namespaces](https://wiki.ubuntu.com/UserNamespace)
is not supported yet. To work around that, you can use the `vagrant lxc sudoers`
command which will create a file under `/etc/sudoers.d/vagrant-lxc` whitelisting
all commands required by `vagrant-lxc` to run.
If you are interested on what will be generated by that command, please check
[this code](lib/vagrant-lxc/command/sudoers.rb).
_vagrant-lxc < 1.0.0 users, please check this [Wiki page](https://github.com/fgrehm/vagrant-lxc/wiki/Avoiding-%27sudo%27-passwords)_
## More information

View file

@ -6,5 +6,9 @@ module Vagrant
def self.source_root
@source_root ||= Pathname.new(File.dirname(__FILE__)).join('..').expand_path
end
def self.sudo_wrapper_path
"/usr/local/bin/vagrant-lxc-wrapper"
end
end
end

View file

@ -4,7 +4,8 @@ require 'vagrant-lxc/action/create'
require 'vagrant-lxc/action/destroy'
require 'vagrant-lxc/action/destroy_confirm'
require 'vagrant-lxc/action/compress_rootfs'
require 'vagrant-lxc/action/fetch_ip_with_lxc_info'
require 'vagrant-lxc/action/fetch_ip_with_lxc_attach'
require 'vagrant-lxc/action/fetch_ip_from_dnsmasq_leases'
require 'vagrant-lxc/action/forced_halt'
require 'vagrant-lxc/action/forward_ports'
require 'vagrant-lxc/action/gc_private_network_bridges'
@ -12,6 +13,7 @@ require 'vagrant-lxc/action/handle_box_metadata'
require 'vagrant-lxc/action/prepare_nfs_settings'
require 'vagrant-lxc/action/prepare_nfs_valid_ids'
require 'vagrant-lxc/action/private_networks'
require 'vagrant-lxc/action/remove_temporary_files'
require 'vagrant-lxc/action/setup_package_files'
require 'vagrant-lxc/action/warn_networks'
@ -124,6 +126,7 @@ module Vagrant
end
b2.use ClearForwardedPorts
b2.use RemoveTemporaryFiles
b2.use GcPrivateNetworkBridges
b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result]
@ -181,7 +184,8 @@ module Vagrant
def self.action_ssh_ip
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::ConfigValidate do |env, b2|
b2.use FetchIpWithLxcInfo
b2.use FetchIpWithLxcAttach if env[:machine].provider.driver.supports_attach?
b2.use FetchIpFromDnsmasqLeases
end
end
end

View file

@ -8,15 +8,11 @@ module Vagrant
def call(env)
@env = env
driver = env[:machine].provider.driver
config = env[:machine].provider_config
utsname = env[:machine].config.vm.hostname || env[:machine].id
if driver.supports_new_config_format
config.customize 'uts.name', utsname
else
config.customize 'utsname', utsname
end
config.customize 'utsname', utsname
# Fix apparmor issues when starting Ubuntu 14.04 containers
# See https://github.com/fgrehm/vagrant-lxc/issues/278 for more information
@ -24,19 +20,8 @@ module Vagrant
config.customize 'mount.entry', '/sys/fs/pstore sys/fs/pstore none bind,optional 0 0'
end
# Make selinux read-only, see
# https://github.com/fgrehm/vagrant-lxc/issues/301
if Dir.exists?('/sys/fs/selinux')
config.customize 'mount.entry', '/sys/fs/selinux sys/fs/selinux none bind,ro 0 0'
end
if config.tmpfs_mount_size && !config.tmpfs_mount_size.empty?
# Make /tmp a tmpfs to prevent init scripts from nuking synced folders mounted in here
config.customize 'mount.entry', "tmpfs tmp tmpfs nodev,nosuid,size=#{config.tmpfs_mount_size} 0 0"
end
env[:ui].info I18n.t("vagrant_lxc.messages.starting")
driver.start(config.customizations)
env[:machine].provider.driver.start(config.customizations)
@app.call env
end

View file

@ -19,28 +19,14 @@ module Vagrant
container_name = generate_container_name(env)
end
backingstore = config.backingstore
if backingstore.nil?
backingstore = config.privileged ? "best" : "dir"
end
driver = env[:machine].provider.driver
template_options = env[:lxc_template_opts]
if driver.supports_new_config_format
if env[:lxc_box_config]
driver.update_config_keys(env[:lxc_box_config])
end
else
template_options['--oldconfig'] = ''
end
driver.create(
env[:machine].provider.driver.create(
container_name,
backingstore,
config.backingstore,
config.backingstore_options,
env[:lxc_template_src],
env[:lxc_template_config],
template_options
env[:lxc_template_opts]
)
driver.update_config_keys
env[:machine].id = container_name

View file

@ -0,0 +1,49 @@
module Vagrant
module LXC
module Action
class FetchIpFromDnsmasqLeases
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_from_dnsmasq_leases")
end
def call(env)
env[:machine_ip] ||= assigned_ip(env)
@app.call(env)
end
def assigned_ip(env)
mac_address = env[:machine].provider.driver.mac_address
ip = nil
10.times do
dnsmasq_leases = read_dnsmasq_leases
@logger.debug "Attempting to load ip from dnsmasq leases (mac: #{mac_address})"
@logger.debug dnsmasq_leases
if dnsmasq_leases =~ /#{Regexp.escape mac_address.to_s}\s+([0-9.]+)\s+/i
ip = $1.to_s
break
else
@logger.debug 'Ip could not be parsed from dnsmasq leases file'
sleep 2
end
end
ip
end
LEASES_PATHS = %w(
/var/lib/misc/dnsmasq.*.leases
/var/lib/misc/dnsmasq.leases
/var/lib/dnsmasq/dnsmasq.leases
/var/db/dnsmasq.leases
/var/lib/libvirt/dnsmasq/*.leases
)
def read_dnsmasq_leases
Dir["{#{LEASES_PATHS.join(',')}}"].map do |file|
File.read(file)
end.join("\n")
end
end
end
end
end

View file

@ -1,31 +1,30 @@
module Vagrant
module LXC
module Action
class FetchIpWithLxcInfo
class FetchIpWithLxcAttach
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_with_lxc_info")
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_with_lxc_attach")
end
def call(env)
env[:machine_ip] ||= assigned_ip(env)
rescue LXC::Errors::NamespacesNotSupported
@logger.info 'The `lxc-attach` command available does not support the --namespaces parameter, falling back to dnsmasq leases to fetch container ip'
ensure
@app.call(env)
end
def assigned_ip(env)
config = env[:machine].provider_config
fetch_ip_tries = config.fetch_ip_tries
driver = env[:machine].provider.driver
ip = ''
return config.ssh_ip_addr if not config.ssh_ip_addr.nil?
retryable(:on => LXC::Errors::ExecuteError, :tries => fetch_ip_tries, :sleep => 3) do
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
unless ip = get_container_ip_from_ip_addr(driver)
# retry
raise LXC::Errors::ExecuteError, :command => "lxc-info"
raise LXC::Errors::ExecuteError, :command => "lxc-attach"
end
end
ip
@ -33,8 +32,8 @@ module Vagrant
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
def get_container_ip_from_ip_addr(driver)
output = driver.info '-iH'
if output =~ /^([0-9.]+)/
output = driver.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: ['network', 'mount']
if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
return $1.to_s
end
end

View file

@ -1,5 +1,3 @@
require 'open3'
module Vagrant
module LXC
module Action
@ -69,9 +67,7 @@ module Vagrant
# TODO: Deprecate this behavior of "automagically" skipping ssh forwarded ports
if type == :forwarded_port && options[:id] != 'ssh'
if options.fetch(:host_ip, '').to_s.strip.empty?
options[:host_ip] = '127.0.0.1'
end
options.delete(:host_ip) if options.fetch(:host_ip, '').to_s.strip.empty?
mappings[options[:host]] = options
end
end
@ -80,12 +76,8 @@ module Vagrant
end
def redirect_port(host_ip, host_port, guest_ip, guest_port)
if redir_version >= 3
params = %W( -n #{host_ip}:#{host_port} #{guest_ip}:#{guest_port} )
else
params = %W( --lport=#{host_port} --caddr=#{guest_ip} --cport=#{guest_port} )
params.unshift "--laddr=#{host_ip}" if host_ip
end
params = %W( --lport=#{host_port} --caddr=#{guest_ip} --cport=#{guest_port} )
params.unshift "--laddr=#{host_ip}" if host_ip
params << '--syslog' if ENV['REDIR_LOG']
if host_port < 1024
redir_cmd = "sudo redir #{params.join(' ')} 2>/dev/null"
@ -105,13 +97,6 @@ module Vagrant
end
end
def redir_version
stdout, stderr, _ = Open3.capture3 "redir --version"
# For some weird reason redir printed version information in STDERR prior to 3.2
version = stdout.empty? ? stderr : stdout
version.split('.')[0].to_i
end
def redir_installed?
system "which redir > /dev/null"
end

View file

@ -35,9 +35,8 @@ module Vagrant
if ! driver.bridge_is_in_use?(bridge)
env[:ui].info I18n.t("vagrant_lxc.messages.remove_bridge", name: bridge)
unless ['lxcbr0', 'virbr0'].include? bridge
driver.remove_bridge(bridge)
end
# TODO: Output that bridge is being removed
driver.remove_bridge(bridge)
end
end
end

View file

@ -33,10 +33,8 @@ module Vagrant
end
if template_config_file.exist?
@env[:lxc_box_config] = template_config_file.to_s
@env[:lxc_template_opts].merge!('--config' => template_config_file.to_s)
elsif old_template_config_file.exist?
@env[:lxc_box_config] = old_template_config_file.to_s
@env[:lxc_template_config] = old_template_config_file.to_s
end

View file

@ -26,19 +26,16 @@ module Vagrant
next if type.to_sym != :private_network
container_name = env[:machine].provider.driver.container_name
address_type = config[:type]
ip = config[:ip]
bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) }
bridge = config.fetch(:lxc__bridge_name)
env[:machine].provider.driver.configure_private_network(bridge, bridge_ip, container_name, address_type, ip)
env[:machine].provider.driver.configure_private_network(bridge, bridge_ip, container_name, ip)
end
end
def build_bridge_ip(ip)
if ip
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
end
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
end
end
end

View file

@ -0,0 +1,23 @@
module Vagrant
module LXC
module Action
class RemoveTemporaryFiles
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::remove_tmp_files")
end
def call(env)
# Continue execution, we need the container to be stopped
@app.call env
if env[:machine].state.id == :stopped
@logger.debug 'Removing temporary files'
tmp_path = env[:machine].provider.driver.rootfs_path.join('tmp')
env[:machine].provider.sudo_wrapper.run('rm', '-rf', "#{tmp_path}/*")
end
end
end
end
end
end

View file

@ -1,8 +1,5 @@
require 'tempfile'
require "vagrant-lxc/driver"
require "vagrant-lxc/sudo_wrapper"
module Vagrant
module LXC
module Command
@ -28,7 +25,7 @@ module Vagrant
argv = parse_options(opts)
return unless argv
wrapper_path = SudoWrapper.dest_path
wrapper_path = Vagrant::LXC.sudo_wrapper_path
wrapper = create_wrapper!
sudoers = create_sudoers!(options[:user], wrapper_path)
@ -46,14 +43,12 @@ module Vagrant
# This requires vagrant 1.5.2+ https://github.com/mitchellh/vagrant/commit/3371c3716278071680af9b526ba19235c79c64cb
def create_wrapper!
lxc_base_path = Driver.new("").containers_path
wrapper = Tempfile.new('lxc-wrapper').tap do |file|
template = Vagrant::Util::TemplateRenderer.new(
'sudoers.rb',
:template_root => Vagrant::LXC.source_root.join('templates').to_s,
:cmd_paths => build_cmd_paths_hash,
:lxc_base_path => lxc_base_path,
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/(?:\\d+?\\.\\d+?\\.\\d+?/)?gems/vagrant-lxc.+/scripts/pipework"
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/gems/vagrant-lxc.+/scripts/pipework"
)
file.puts template.render
end
@ -85,10 +80,9 @@ module Vagrant
def build_cmd_paths_hash
{}.tap do |hash|
%w( which cat mkdir cp chown chmod rm tar chown ip ifconfig brctl ).each do |cmd|
hash[cmd] = `sudo which #{cmd}`.strip
hash[cmd] = `which #{cmd}`.strip
end
hash['lxc_bin'] = Pathname(`sudo which lxc-create`.strip).parent.to_s
hash['ruby'] = Gem.ruby
hash['lxc_bin'] = Pathname(`which lxc-create`.strip).parent.to_s
end
end
end

View file

@ -18,29 +18,12 @@ module Vagrant
# machine name, set this to :machine
attr_accessor :container_name
# Size (as a string like '400M') of the tmpfs to mount at /tmp on boot.
# Set to false or nil to disable the tmpfs mount altogether. Defaults to '2G'.
attr_accessor :tmpfs_mount_size
attr_accessor :fetch_ip_tries
attr_accessor :ssh_ip_addr
# Whether the container needs to be privileged. Defaults to true (unprivileged containers
# is a very new feature in vagrant-lxc). If false, will try creating an unprivileged
# container. If it can't, will revert to the old "sudo wrapper" method to create a privileged
# container.
attr_accessor :privileged
def initialize
@customizations = []
@backingstore = UNSET_VALUE
@backingstore_options = []
@sudo_wrapper = UNSET_VALUE
@container_name = UNSET_VALUE
@tmpfs_mount_size = UNSET_VALUE
@fetch_ip_tries = UNSET_VALUE
@ssh_ip_addr = UNSET_VALUE
@privileged = UNSET_VALUE
end
# Customize the container by calling `lxc-start` with the given
@ -64,13 +47,10 @@ module Vagrant
end
def finalize!
@sudo_wrapper = nil if @sudo_wrapper == UNSET_VALUE
@container_name = nil if @container_name == UNSET_VALUE
@backingstore = nil if @backingstore == UNSET_VALUE
@backingstore = "best" if @backingstore == UNSET_VALUE
@existing_container_name = nil if @existing_container_name == UNSET_VALUE
@tmpfs_mount_size = '2G' if @tmpfs_mount_size == UNSET_VALUE
@fetch_ip_tries = 10 if @fetch_ip_tries == UNSET_VALUE
@ssh_ip_addr = nil if @ssh_ip_addr == UNSET_VALUE
@privileged = true if @privileged == UNSET_VALUE
end
end
end

View file

@ -3,7 +3,6 @@ require "vagrant/util/subprocess"
require "vagrant-lxc/errors"
require "vagrant-lxc/driver/cli"
require "vagrant-lxc/sudo_wrapper"
require "etc"
@ -17,13 +16,15 @@ module Vagrant
class ContainerNotFound < StandardError; end
# Default root folder where container configs are stored
DEFAULT_CONTAINERS_PATH = '/var/lib/lxc'
attr_reader :container_name,
:customizations
def initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true)
def initialize(container_name, sudo_wrapper, cli = nil)
@container_name = container_name
@sudo_wrapper = sudo_wrapper || SudoWrapper.new(privileged: privileged)
@cli = cli || CLI.new(@sudo_wrapper, container_name)
@sudo_wrapper = sudo_wrapper
@cli = cli || CLI.new(sudo_wrapper, container_name)
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
@customizations = []
end
@ -34,7 +35,7 @@ module Vagrant
# Root folder where container configs are stored
def containers_path
@containers_path ||= @cli.config('lxc.lxcpath')
@containers_path ||= @cli.support_config_command? ? @cli.config('lxc.lxcpath') : DEFAULT_CONTAINERS_PATH
end
def all_containers
@ -45,25 +46,21 @@ module Vagrant
Pathname.new("#{containers_path}/#{@container_name}")
end
def config_path
base_path.join('config').to_s
end
def rootfs_path
pathtype, path = config_string.match(/^lxc\.rootfs(?:\.path)?\s+=\s+(.+:)?(.+)$/)[1..2]
case pathtype
when 'overlayfs:'
config_entry = config_string.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]
case config_entry
when /^overlayfs:/
# Split on colon (:), ignoring any colon escaped by an escape character ( \ )
# Pays attention to when the escape character is itself escaped.
_, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
fs_type, master_path, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
if overlay_path
Pathname.new(overlay_path)
else
# Malformed: fall back to prior behaviour
Pathname.new(path)
Pathname.new(config_entry)
end
else
Pathname.new(path)
Pathname.new(config_entry)
end
end
@ -76,27 +73,40 @@ module Vagrant
end
def config_string
@sudo_wrapper.run('cat', config_path)
@sudo_wrapper.run('cat', base_path.join('config').to_s)
end
def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {})
@cli.name = @container_name = name
@logger.debug "Creating container..."
@cli.create template_path, backingstore, backingstore_options, config_file, template_options
import_template(template_path) do |template_name|
@logger.debug "Creating container..."
@cli.create template_name, backingstore, backingstore_options, config_file, template_options
end
end
def share_folders(folders)
folders.each do |f|
share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, nil))
share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, 'bind'))
end
end
def share_folder(host_path, guest_path, mount_options = nil)
guest_path = guest_path.gsub(/^\//, '').gsub(' ', '\\\040')
mount_options = Array(mount_options || ['bind', 'create=dir'])
guest_path = guest_path.gsub(/^\//, '')
guest_full_path = rootfs_path.join(guest_path)
unless guest_full_path.directory?
begin
@logger.debug("Guest path doesn't exist, creating: #{guest_full_path}")
@sudo_wrapper.run('mkdir', '-p', guest_full_path.to_s)
rescue Errno::EACCES
raise Vagrant::Errors::SharedFolderCreateFailed, :path => guest_path.to_s
end
end
mount_options = Array(mount_options || ['bind'])
host_path = host_path.to_s.gsub(' ', '\\\040')
guest_path = guest_path.gsub(' ', '\\\040')
@customizations << ['mount.entry', "#{host_path} #{guest_path} none #{mount_options.join(',')} 0 0"]
end
@ -122,38 +132,25 @@ module Vagrant
@cli.destroy
end
def supports_attach?
@cli.supports_attach?
end
def attach(*command)
@cli.attach(*command)
end
def info(*command)
@cli.info(*command)
end
def configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip)
def configure_private_network(bridge_name, bridge_ip, container_name, ip)
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
if ip
ip += '/24'
end
if ! bridge_exists?(bridge_name)
if not bridge_ip
raise "Bridge is missing and no IP was specified!"
end
@logger.info "Creating the bridge #{bridge_name}"
cmd = [
'brctl',
'addbr',
bridge_name
]
@sudo_wrapper.run(*cmd)
end
cmd = [
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
bridge_name,
container_name,
"#{ip}/24"
]
@sudo_wrapper.run(*cmd)
if ! bridge_has_an_ip?(bridge_name)
if not bridge_ip
raise "Bridge has no IP and none was specified!"
end
@logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
cmd = [
'ip',
@ -164,16 +161,7 @@ module Vagrant
bridge_name
]
@sudo_wrapper.run(*cmd)
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'up')
end
cmd = [
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
bridge_name,
container_name,
ip ||= "dhcp"
]
@sudo_wrapper.run(*cmd)
end
def bridge_has_an_ip?(bridge_name)
@ -181,12 +169,6 @@ module Vagrant
`ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
end
def bridge_exists?(bridge_name)
@logger.info "Checking whether bridge #{bridge_name} exists"
brctl_output = `ip link | egrep -q " #{bridge_name}:"`
$?.to_i == 0
end
def bridge_is_in_use?(bridge_name)
# REFACTOR: This method is **VERY** hacky
@logger.info "Checking if bridge #{bridge_name} is in use"
@ -195,15 +177,12 @@ module Vagrant
end
def remove_bridge(bridge_name)
if ['lxcbr0', 'virbr0'].include? bridge_name
@logger.info "Skipping removal of system bridge #{bridge_name}"
return
end
return unless bridge_exists?(bridge_name)
@logger.info "Checking whether bridge #{bridge_name} exists"
brctl_output = `ifconfig -a | grep -q #{bridge_name}`
return if $?.to_i != 0
@logger.info "Removing bridge #{bridge_name}"
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'down')
@sudo_wrapper.run('ifconfig', bridge_name, 'down')
@sudo_wrapper.run('brctl', 'delbr', bridge_name)
end
@ -211,10 +190,6 @@ module Vagrant
@version ||= @cli.version
end
def supports_new_config_format
Gem::Version.new(version) >= Gem::Version.new('2.1.0')
end
# TODO: This needs to be reviewed and specs needs to be written
def compress_rootfs
# TODO: Pass in tmpdir so we can clean up from outside
@ -245,13 +220,6 @@ module Vagrant
write_config(contents)
end
def update_config_keys(path = nil)
path = path || config_path
@cli.update_config(path)
rescue Errors::ExecuteError
# not on LXC 2.1+. Doesn't matter, ignore.
end
protected
def write_customizations(customizations)
@ -268,23 +236,47 @@ module Vagrant
end
def write_config(contents)
confpath = base_path.join('config').to_s
begin
File.open(confpath, File::RDWR) do |file|
file.write contents
end
rescue
# We don't have permissions to write in the conf file. That's probably because it's a
# privileged container. Work around that through sudo_wrapper.
Tempfile.new('lxc-config').tap do |file|
file.chmod 0644
file.write contents
file.close
@sudo_wrapper.run 'cp', '-f', file.path, confpath
@sudo_wrapper.run 'chown', 'root:root', confpath
end
Tempfile.new('lxc-config').tap do |file|
file.chmod 0644
file.write contents
file.close
@sudo_wrapper.run 'cp', '-f', file.path, base_path.join('config').to_s
@sudo_wrapper.run 'chown', 'root:root', base_path.join('config').to_s
end
end
def import_template(path)
template_name = "vagrant-tmp-#{@container_name}"
tmp_template_path = templates_path.join("lxc-#{template_name}").to_s
@logger.info 'Copying LXC template into place'
@sudo_wrapper.run('cp', path, tmp_template_path)
@sudo_wrapper.run('chmod', '+x', tmp_template_path)
yield template_name
ensure
@logger.info 'Removing LXC template'
if tmp_template_path
@sudo_wrapper.run('rm', tmp_template_path)
end
end
TEMPLATES_PATH_LOOKUP = %w(
/usr/share/lxc/templates
/usr/lib/lxc/templates
/usr/lib64/lxc/templates
/usr/local/lib/lxc/templates
)
def templates_path
return @templates_path if @templates_path
path = TEMPLATES_PATH_LOOKUP.find { |candidate| File.directory?(candidate) }
if !path
raise Errors::TemplatesDirMissing.new paths: TEMPLATES_PATH_LOOKUP.inspect
end
@templates_path = Pathname(path)
end
end
end
end

View file

@ -29,7 +29,7 @@ module Vagrant
def version
return @version if @version
@version = run(:create, '--version')
@version = support_version_command? ? run(:version) : run(:create, '--version')
if @version =~ /(lxc version:\s+|)(.+)\s*$/
@version = $2.downcase
else
@ -39,11 +39,11 @@ module Vagrant
end
def config(param)
run(:config, param).gsub("\n", '')
end
def update_config(path)
run('update-config', '-c', path)
if support_config_command?
run(:config, param).gsub("\n", '')
else
raise Errors::CommandNotSupported, name: 'config', available_version: '> 1.x.x', version: version
end
end
def state
@ -59,7 +59,7 @@ module Vagrant
config_opts = ['-f', config_file]
end
extra = template_opts.to_a.flatten.reject { |elem| elem.empty? }
extra = template_opts.to_a.flatten
extra.unshift '--' unless extra.empty?
run :create,
@ -85,19 +85,9 @@ module Vagrant
run :start, '-d', '--name', @name, *Array(options)
end
## lxc-stop will exit 2 if machine was already stopped
# Man Page:
# 2 The specified container exists but was not running.
def stop
begin
run :stop, '--name', @name
rescue LXC::Errors::ExecuteError => e
if e.exitcode == 2
@logger.debug "Machine already stopped, lxc-stop returned 2"
else
raise e
end
end
attach '/sbin/halt' if supports_attach?
run :stop, '--name', @name
end
def attach(*cmd)
@ -113,17 +103,17 @@ module Vagrant
end
if namespaces
extra = ['--namespaces', namespaces]
if supports_attach_with_namespaces?
extra = ['--namespaces', namespaces]
else
raise LXC::Errors::NamespacesNotSupported
end
end
end
run :attach, '--name', @name, *((extra || []) + cmd)
end
def info(*cmd)
run(:info, '--name', @name, *cmd)
end
def transition_to(target_state, tries = 30, timeout = 1, &block)
raise TransitionBlockNotProvided unless block_given?
@ -141,11 +131,42 @@ module Vagrant
end
end
def supports_attach?
unless defined?(@supports_attach)
begin
@supports_attach = true
run(:attach, '--name', @name, '--', '/bin/true')
rescue LXC::Errors::ExecuteError
@supports_attach = false
end
end
return @supports_attach
end
def support_config_command?
version[0].to_i >= 1
end
def support_version_command?
@sudo_wrapper.run('which', 'lxc-version').strip.chomp != ''
rescue Vagrant::LXC::Errors::ExecuteError
return false
end
private
def run(command, *args)
@sudo_wrapper.run("lxc-#{command}", *args)
end
def supports_attach_with_namespaces?
unless defined?(@supports_attach_with_namespaces)
@supports_attach_with_namespaces = run(:attach, '-h', :show_stderr => true).values.join.include?('--namespaces')
end
return @supports_attach_with_namespaces
end
end
end
end

View file

@ -5,26 +5,19 @@ module Vagrant
module Errors
class ExecuteError < Vagrant::Errors::VagrantError
error_key(:lxc_execute_error)
attr_reader :stderr, :stdout, :exitcode
attr_reader :stderr, :stdout
def initialize(message, *args)
super
if message.is_a?(Hash)
@stderr = message[:stderr]
@stdout = message[:stdout]
@exitcode = message[:exitcode]
end
end
end
# Raised when user interrupts a subprocess
class SubprocessInterruptError < Vagrant::Errors::VagrantError
error_key(:lxc_interrupt_error)
def initialize(message, *args)
super
end
class NamespacesNotSupported < Vagrant::Errors::VagrantError
end
class LxcLinuxRequired < Vagrant::Errors::VagrantError
error_key(:lxc_linux_required)
end

View file

@ -10,25 +10,26 @@ module Vagrant
EOF
provider(:lxc, parallel: true, priority: 7) do
require_relative 'provider'
init!
require File.expand_path("../provider", __FILE__)
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../../locales/en.yml')
I18n.reload!
Provider
end
command "lxc" do
require_relative 'command/root'
init!
Command::Root
end
config(:lxc, :provider) do
require_relative 'config'
init!
require File.expand_path("../config", __FILE__)
Config
end
synced_folder(:lxc) do
require_relative 'synced_folder'
require File.expand_path("../synced_folder", __FILE__)
SyncedFolder
end
@ -36,16 +37,6 @@ module Vagrant
require_relative "provider/cap/public_address"
Provider::Cap::PublicAddress
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../../locales/en.yml')
I18n.reload!
@_init = true
end
end
end
end

View file

@ -2,6 +2,7 @@ require "log4r"
require "vagrant-lxc/action"
require "vagrant-lxc/driver"
require "vagrant-lxc/sudo_wrapper"
module Vagrant
module LXC
@ -24,9 +25,18 @@ module Vagrant
machine_id_changed
end
def sudo_wrapper
@shell ||= begin
wrapper = Pathname.new(LXC.sudo_wrapper_path).exist? &&
LXC.sudo_wrapper_path || nil
@logger.debug("Found sudo wrapper : #{wrapper}") if wrapper
SudoWrapper.new(wrapper)
end
end
def ensure_lxc_installed!
begin
SudoWrapper.new(privileged: @machine.provider_config.privileged).run("which", "lxc-create")
sudo_wrapper.run("/usr/bin/which", "lxc-create")
rescue Vagrant::LXC::Errors::ExecuteError
raise Errors::LxcNotInstalled
end
@ -39,7 +49,7 @@ module Vagrant
begin
@logger.debug("Instantiating the container for: #{id.inspect}")
@driver = Driver.new(id, privileged: @machine.provider_config.privileged)
@driver = Driver.new(id, self.sudo_wrapper)
@driver.validate!
rescue Driver::ContainerNotFound
# The container doesn't exist, so we probably have a stale

View file

@ -6,41 +6,15 @@ module Vagrant
attr_reader :wrapper_path
def self.dest_path
"/usr/local/bin/vagrant-lxc-wrapper"
end
def initialize(privileged: true)
@wrapper_path = Pathname.new(SudoWrapper.dest_path).exist? && SudoWrapper.dest_path || nil
@privileged = privileged
def initialize(wrapper_path = nil)
@wrapper_path = wrapper_path
@logger = Log4r::Logger.new("vagrant::lxc::sudo_wrapper")
end
def run(*command)
options = command.last.is_a?(Hash) ? command.last : {}
# Avoid running LXC commands with a restrictive umask.
# Otherwise disasters occur, like the container root directory
# having permissions `rwxr-x---` which prevents the `vagrant`
# user from accessing its own home directory; among other
# problems, SSH cannot then read `authorized_keys`!
old_mask = File.umask
File.umask(old_mask & 022) # allow all `r` and `x` bits
begin
if @privileged
if @wrapper_path && !options[:no_wrapper]
command.unshift @wrapper_path
execute *(['sudo'] + command)
else
execute *(['sudo', '/usr/bin/env'] + command)
end
else
execute *(['/usr/bin/env'] + command)
end
ensure
File.umask(old_mask)
end
command.unshift @wrapper_path if @wrapper_path && !options[:no_wrapper]
execute *(['sudo'] + command)
end
private
@ -68,10 +42,10 @@ module Vagrant
# nicely handled by Vagrant.
if r.exit_code != 0
if @interrupted
raise LXC::Errors::SubprocessInterruptError, command.inspect
@logger.info("Exit code != 0, but interrupted. Ignoring.")
else
raise LXC::Errors::ExecuteError,
command: command.inspect, stderr: r.stderr, stdout: r.stdout, exitcode: r.exit_code
command: command.inspect, stderr: r.stderr, stdout: r.stdout
end
end
end

View file

@ -1,5 +1,5 @@
module Vagrant
module LXC
VERSION = "1.4.2"
VERSION = "1.1.0"
end
end

View file

@ -40,9 +40,6 @@ en:
preparing: Setting up mount entries for shared folders...
errors:
lxc_interrupt_error: |-
Interrupted
lxc_execute_error: |-
There was an error executing %{command}
@ -67,7 +64,7 @@ en:
another provider.
lxc_not_installed: |-
The `lxc` package does not seem to be installed or `lxc-create` is not accessible on the PATH.
The `lxc` package does not seem to be installed or is not accessible on the PATH.
lxc_redir_not_installed: |-
`redir` is not installed or is not accessible on the PATH.

View file

@ -1,4 +1,4 @@
#!/usr/bin/env bash
#!/bin/bash
# This is a modified version of /usr/share/lxc/templates/lxc-download
# that comes with ubuntu-lxc 1.0.0 stable from ppa changed to suit vagrant-lxc needs
@ -33,10 +33,8 @@ LXC_PATH=
LXC_ROOTFS=
LXC_TARBALL=
LXC_CONFIG=
LXC_USE_OLDCONFIG=
LXC_STRIP_COMPONENTS=2
usage() {
cat <<EOF
vagrant-lxc default template
@ -46,7 +44,6 @@ Required arguments:
Optional arguments:
[ --config ]: Configuration file to be used when building the container
[ --oldconfig ]: Use pre LXC 2.1 config format
[ -h | --help ]: This help message
LXC internal arguments (do not pass manually!):
@ -60,7 +57,7 @@ EOF
return 0
}
options=$(getopt -o h -l tarball:,config:,oldconfig,help:,name:,path:,rootfs:,mapped-uid:,mapped-gid:,strip-components: -- "$@")SS
options=$(getopt -o h -l tarball:,config:,help:,name:,path:,rootfs:,mapped-uid:,mapped-gid:,strip-components: -- "$@")SS
if [ $? -ne 0 ]; then
usage $(basename $0)
@ -73,7 +70,6 @@ do
case "$1" in
-h|--help) usage $0 && exit 0;;
--config) LXC_CONFIG=$2; shift 2;;
--oldconfig) LXC_USE_OLDCONFIG=1; shift 1;;
--tarball) LXC_TARBALL=$2; shift 2;;
--name) LXC_NAME=$2; shift 2;;
--path) LXC_PATH=$2; shift 2;;
@ -100,6 +96,11 @@ if [ -z "${LXC_PATH}" ]; then
exit 1
fi
if [ -z "${LXC_CONFIG}" ]; then
echo "'config' parameter is required"
exit 1
fi
# if $LXC_ROOTFS exists here, it was passed in with --rootfs
if [ -z "${LXC_ROOTFS}" ]; then
config=${LXC_PATH}/config
@ -114,6 +115,7 @@ fi
# Unpack the rootfs
echo "Unpacking the rootfs"
mkdir -p /var/lock/subsys
(
flock -x 200
if [ $? -ne 0 ]; then
@ -122,14 +124,13 @@ echo "Unpacking the rootfs"
fi
mkdir -p ${LXC_ROOTFS}
(cd ${LXC_ROOTFS} && tar xfz ${LXC_TARBALL} --strip-components=${LXC_STRIP_COMPONENTS} --xattrs --xattrs-include=* || true)
if [ ! -f ${LXC_ROOTFS}/bin/true ]; then
(cd ${LXC_ROOTFS} && tar xfz ${LXC_TARBALL} --strip-components=${LXC_STRIP_COMPONENTS})
if [ $? -ne 0 ]; then
echo "Failed to extract rootfs"
exit 1
fi
) 200>${LXC_PATH}/vagrant_lock
rm ${LXC_PATH}/vagrant_lock
) 200>/var/lock/subsys/lxc
mkdir -p ${LXC_ROOTFS}/dev/pts/
@ -148,12 +149,7 @@ if [ -e "${LXC_PATH}/config-auto" ]; then
cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
rm ${LXC_PATH}/config-auto
fi
if [ $LXC_USE_OLDCONFIG ]; then
echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
else
echo "lxc.uts.name = ${LXC_NAME}" >> ${LXC_PATH}/config
fi
echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
## Re-add the previously removed network config
if [ -e "${LXC_PATH}/config-network" ]; then
@ -164,13 +160,11 @@ if [ -e "${LXC_PATH}/config-network" ]; then
rm ${LXC_PATH}/config-network
fi
if [ -n "${LXC_CONFIG}" ]; then
## Append the defaults
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# vagrant-lxc base box specific configuration" >> ${LXC_PATH}/config
cat ${LXC_CONFIG} >> ${LXC_PATH}/config
fi
## Append the defaults
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# vagrant-lxc base box specific configuration" >> ${LXC_PATH}/config
cat ${LXC_CONFIG} >> ${LXC_PATH}/config
# Empty section for lxc.customize calls from vagrantfile
echo "" >> ${LXC_PATH}/config

View file

@ -1,12 +1,13 @@
#!/bin/sh
# This code should (try to) follow Google's Shell Style Guide
# (https://google-styleguide.googlecode.com/svn/trunk/shell.xml)
#!/bin/bash
# Borrowed from https://github.com/jpetazzo/pipework
set -e
case "$1" in
--wait)
WAIT=1
;;
--wait)
WAIT=1
;;
esac
IFNAME=$1
@ -18,405 +19,280 @@ if [ "$2" = "-i" ]; then
shift 2
fi
if [ "$2" = "-l" ]; then
LOCAL_IFNAME=$3
shift 2
fi
GUESTNAME=$2
IPADDR=$3
MACADDR=$4
case "$MACADDR" in
*@*)
VLAN="${MACADDR#*@}"
VLAN="${VLAN%%@*}"
MACADDR="${MACADDR%%@*}"
;;
*)
VLAN=
;;
esac
# did they ask to generate a custom MACADDR?
# generate the unique string
case "$MACADDR" in
U:*)
macunique="${MACADDR#*:}"
# now generate a 48-bit hash string from $macunique
MACADDR=$(echo $macunique|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
;;
esac
if echo $MACADDR | grep -q @
then
VLAN=$(echo $MACADDR | cut -d@ -f2)
MACADDR=$(echo $MACADDR | cut -d@ -f1)
else
VLAN=
fi
[ "$IPADDR" ] || [ "$WAIT" ] || {
echo "Syntax:"
echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> dhcp [macaddr][@vlan]"
echo "pipework route <guest> <route_command>"
echo "pipework --wait [-i containerinterface]"
exit 1
echo "Syntax:"
echo "pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
echo "pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]"
echo "pipework --wait [-i containerinterface]"
exit 1
}
# Succeed if the given utility is installed. Fail otherwise.
# For explanations about `which` vs `type` vs `command`, see:
# http://stackoverflow.com/questions/592620/check-if-a-program-exists-from-a-bash-script/677212#677212
# (Thanks to @chenhanxiao for pointing this out!)
installed () {
command -v "$1" >/dev/null 2>&1
}
# Google Styleguide says error messages should go to standard error.
warn () {
echo "$@" >&2
}
die () {
status="$1"
shift
warn "$@"
exit "$status"
}
# First step: determine type of first argument (bridge, physical interface...),
# Unless "--wait" is set (then skip the whole section)
if [ -z "$WAIT" ]; then
if [ -d "/sys/class/net/$IFNAME" ]
then
if [ -d "/sys/class/net/$IFNAME/bridge" ]; then
IFTYPE=bridge
BRTYPE=linux
elif installed ovs-vsctl && ovs-vsctl list-br|grep -q "^${IFNAME}$"; then
IFTYPE=bridge
BRTYPE=openvswitch
elif [ "$(cat "/sys/class/net/$IFNAME/type")" -eq 32 ]; then # InfiniBand IPoIB interface type 32
IFTYPE=ipoib
# The IPoIB kernel module is fussy, set device name to ib0 if not overridden
CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
PKEY=$VLAN
else IFTYPE=phys
fi
else
case "$IFNAME" in
br*)
IFTYPE=bridge
BRTYPE=linux
;;
ovs*)
if ! installed ovs-vsctl; then
die 1 "Need OVS installed on the system to create an ovs bridge"
# First step: determine type of first argument (bridge, physical interface...), skip if --wait set
if [ -z "$WAIT" ]; then
if [ -d /sys/class/net/$IFNAME ]
then
if [ -d /sys/class/net/$IFNAME/bridge ]
then
IFTYPE=bridge
BRTYPE=linux
elif $(which ovs-vsctl >/dev/null 2>&1) && $(ovs-vsctl list-br|grep -q ^$IFNAME$)
then
IFTYPE=bridge
BRTYPE=openvswitch
elif [ $(cat /sys/class/net/$IFNAME/type) -eq 32 ]; # Infiniband IPoIB interface type 32
then
IFTYPE=ipoib
# The IPoIB kernel module is fussy, set device name to ib0 if not overridden
CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
else IFTYPE=phys
fi
IFTYPE=bridge
BRTYPE=openvswitch
;;
route*)
IFTYPE=route
;;
dummy*)
IFTYPE=dummy
;;
*) die 1 "I do not know how to setup interface $IFNAME." ;;
esac
fi
else
# case "$IFNAME" in
# br*)
IFTYPE=bridge
BRTYPE=linux
# ;;
# ovs*)
# if ! $(which ovs-vsctl >/dev/null)
# then
# echo "Need OVS installed on the system to create an ovs bridge"
# exit 1
# fi
# IFTYPE=bridge
# BRTYPE=openvswitch
# ;;
# *)
# echo "I do not know how to setup interface $IFNAME."
# exit 1
# ;;
# esac
fi
fi
# Set the default container interface name to eth1 if not already set
CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1}
[ "$WAIT" ] && {
while true; do
# This first method works even without `ip` or `ifconfig` installed,
# but doesn't work on older kernels (e.g. CentOS 6.X). See #128.
grep -q '^1$' "/sys/class/net/$CONTAINER_IFNAME/carrier" && break
# This method hopefully works on those older kernels.
ip link ls dev "$CONTAINER_IFNAME" && break
sleep 1
done > /dev/null 2>&1
while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null
do sleep 1
done
exit 0
}
[ "$IFTYPE" = bridge ] && [ "$BRTYPE" = linux ] && [ "$VLAN" ] && {
die 1 "VLAN configuration currently unsupported for Linux bridge."
[ $IFTYPE = bridge ] && [ $BRTYPE = linux ] && [ "$VLAN" ] && {
echo "VLAN configuration currently unsupported for Linux bridge."
exit 1
}
[ "$IFTYPE" = ipoib ] && [ "$MACADDR" ] && {
die 1 "MACADDR configuration unsupported for IPoIB interfaces."
[ $IFTYPE = ipoib ] && [ $MACADDR ] && {
echo "MACADDR configuration unsupported for IPoIB interfaces."
exit 1
}
# Second step: find the guest (for now, we only support LXC containers)
while read _ mnt fstype options _; do
[ "$fstype" != "cgroup" ] && continue
echo "$options" | grep -qw devices || continue
CGROUPMNT=$mnt
while read dev mnt fstype options dump fsck
do
[ "$fstype" != "cgroup" ] && continue
echo $options | grep -qw devices || continue
CGROUPMNT=$mnt
done < /proc/mounts
[ "$CGROUPMNT" ] || {
die 1 "Could not locate cgroup mount point."
echo "Could not locate cgroup mount point."
exit 1
}
# Try to find a cgroup matching exactly the provided name.
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
case "$N" in
0)
# If we didn't find anything, try to lookup the container with Docker.
if installed docker; then
RETRIES=3
while [ "$RETRIES" -gt 0 ]; do
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' "$GUESTNAME")
[ "$DOCKERPID" != 0 ] && break
sleep 1
RETRIES=$((RETRIES - 1))
done
0)
# If we didn't find anything, try to lookup the container with Docker.
if which docker >/dev/null
then
RETRIES=3
while [ $RETRIES -gt 0 ]; do
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $GUESTNAME)
[ $DOCKERPID != 0 ] && break
sleep 1
RETRIES=$((RETRIES - 1))
done
[ "$DOCKERPID" = 0 ] && {
die 1 "Docker inspect returned invalid PID 0"
}
[ "$DOCKERPID" = 0 ] && {
echo "Docker inspect returned invalid PID 0"
exit 1
}
[ "$DOCKERPID" = "<no value>" ] && {
die 1 "Container $GUESTNAME not found, and unknown to Docker."
}
else
die 1 "Container $GUESTNAME not found, and Docker not installed."
fi
;;
1) true ;;
*) die 1 "Found more than one container matching $GUESTNAME." ;;
[ "$DOCKERPID" = "<no value>" ] && {
echo "Container $GUESTNAME not found, and unknown to Docker."
exit 1
}
else
echo "Container $GUESTNAME not found, and Docker not installed."
exit 1
fi
;;
1)
true
;;
*)
echo "Found more than one container matching $GUESTNAME."
exit 1
;;
esac
# only check IPADDR if we are not in a route mode
[ "$IFTYPE" != route ] && {
case "$IPADDR" in
# Let's check first if the user asked for DHCP allocation.
dhcp|dhcp:*)
# Use Docker-specific strategy to run the DHCP client
# from the busybox image, in the network namespace of
# the container.
if ! [ "$DOCKERPID" ]; then
warn "You asked for a Docker-specific DHCP method."
warn "However, $GUESTNAME doesn't seem to be a Docker container."
warn "Try to replace 'dhcp' with another option?"
die 1 "Aborting."
fi
DHCP_CLIENT=${IPADDR%%:*}
;;
udhcpc|udhcpc:*|udhcpc-f|udhcpc-f:*|dhcpcd|dhcpcd:*|dhclient|dhclient:*|dhclient-f|dhclient-f:*)
DHCP_CLIENT=${IPADDR%%:*}
# did they ask for the client to remain?
DHCP_FOREGROUND=
[ "${DHCP_CLIENT: -2}" = '-f' ] && {
DHCP_FOREGROUND=true
}
DHCP_CLIENT=${DHCP_CLIENT%-f}
if ! installed "$DHCP_CLIENT"; then
die 1 "You asked for DHCP client $DHCP_CLIENT, but I can't find it."
fi
;;
# Alright, no DHCP? Then let's see if we have a subnet *and* gateway.
*/*@*)
GATEWAY="${IPADDR#*@}" GATEWAY="${GATEWAY%%@*}"
IPADDR="${IPADDR%%@*}"
;;
# No gateway? We need at least a subnet, anyway!
*/*) : ;;
# ... No? Then stop right here.
*)
warn "The IP address should include a netmask."
die 1 "Maybe you meant $IPADDR/24 ?"
;;
esac
}
# If a DHCP method was specified, extract the DHCP options.
if [ "$DHCP_CLIENT" ]; then
case "$IPADDR" in
*:*) DHCP_OPTIONS="${IPADDR#*:}" ;;
esac
if [ "$IPADDR" = "dhcp" ]
then
# Check for first available dhcp client
DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient"
for CLIENT in $DHCP_CLIENT_LIST; do
which $CLIENT >/dev/null && {
DHCP_CLIENT=$CLIENT
break
}
done
[ -z $DHCP_CLIENT ] && {
echo "You asked for DHCP; but no DHCP client could be found."
exit 1
}
else
# Check if a subnet mask was provided.
echo $IPADDR | grep -q / || {
echo "The IP address should include a netmask."
echo "Maybe you meant $IPADDR/24 ?"
exit 1
}
# Check if a gateway address was provided.
if echo $IPADDR | grep -q @
then
GATEWAY=$(echo $IPADDR | cut -d@ -f2)
IPADDR=$(echo $IPADDR | cut -d@ -f1)
else
GATEWAY=
fi
fi
if [ "$DOCKERPID" ]; then
if [ $DOCKERPID ]; then
NSPID=$DOCKERPID
else
NSPID=$(head -n 1 "$(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks")
NSPID=$(head -n 1 $(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks)
[ "$NSPID" ] || {
# it is an alternative way to get the pid
NSPID=$(lxc-info -n "$GUESTNAME" | grep PID | grep -Eo '[0-9]+')
[ "$NSPID" ] || {
die 1 "Could not find a process inside container $GUESTNAME."
}
echo "Could not find a process inside container $GUESTNAME."
exit 1
}
fi
# Check if an incompatible VLAN device already exists
[ "$IFTYPE" = phys ] && [ "$VLAN" ] && [ -d "/sys/class/net/$IFNAME.VLAN" ] && {
ip -d link show "$IFNAME.$VLAN" | grep -q "vlan.*id $VLAN" || {
die 1 "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN"
}
[ $IFTYPE = phys ] && [ "$VLAN" ] && [ -d /sys/class/net/$IFNAME.VLAN ] && {
[ -z "$(ip -d link show $IFNAME.$VLAN | grep "vlan.*id $VLAN")" ] && {
echo "$IFNAME.VLAN already exists but is not a VLAN device for tag $VLAN"
exit 1
}
}
[ ! -d /var/run/netns ] && mkdir -p /var/run/netns
rm -f "/var/run/netns/$NSPID"
ln -s "/proc/$NSPID/ns/net" "/var/run/netns/$NSPID"
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID
ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID
# Check if we need to create a bridge.
[ "$IFTYPE" = bridge ] && [ ! -d "/sys/class/net/$IFNAME" ] && {
[ "$BRTYPE" = linux ] && {
(ip link add dev "$IFNAME" type bridge > /dev/null 2>&1) || (brctl addbr "$IFNAME")
ip link set "$IFNAME" up
}
[ "$BRTYPE" = openvswitch ] && {
ovs-vsctl add-br "$IFNAME"
}
[ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && {
[ $BRTYPE = linux ] && {
(ip link add dev $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME)
ip link set $IFNAME up
}
[ $BRTYPE = openvswitch ] && {
ovs-vsctl add-br $IFNAME
}
}
[ "$IFTYPE" != "route" ] && [ "$IFTYPE" != "dummy" ] && MTU=$(ip link show "$IFNAME" | awk '{print $5}')
MTU=$(ip link show $IFNAME | awk '{print $5}')
# If it's a bridge, we need to create a veth pair
[ "$IFTYPE" = bridge ] && {
if [ -z "$LOCAL_IFNAME" ]; then
[ $IFTYPE = bridge ] && {
LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}"
fi
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
# Does the link already exist?
if ip link show "$LOCAL_IFNAME" >/dev/null 2>&1; then
# link exists, is it in use?
if ip link show "$LOCAL_IFNAME" up | grep -q "UP"; then
echo "Link $LOCAL_IFNAME exists and is up"
exit 1
fi
# delete the link so we can re-add it afterwards
ip link del "$LOCAL_IFNAME"
fi
ip link add name "$LOCAL_IFNAME" mtu "$MTU" type veth peer name "$GUEST_IFNAME" mtu "$MTU"
case "$BRTYPE" in
linux)
(ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) || (brctl addif "$IFNAME" "$LOCAL_IFNAME")
;;
openvswitch)
if ! ovs-vsctl list-ports "$IFNAME" | grep -q "^${LOCAL_IFNAME}$"; then
ovs-vsctl add-port "$IFNAME" "$LOCAL_IFNAME" ${VLAN:+tag="$VLAN"}
fi
;;
esac
ip link set "$LOCAL_IFNAME" up
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU
case "$BRTYPE" in
linux)
(ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME)
;;
openvswitch)
ovs-vsctl add-port $IFNAME $LOCAL_IFNAME ${VLAN:+"tag=$VLAN"}
;;
esac
ip link set $LOCAL_IFNAME up
}
# Note: if no container interface name was specified, pipework will default to ib0
# Note: no macvlan subinterface or ethernet bridge can be created against an
# ipoib interface. Infiniband is not ethernet. ipoib is an IP layer for it.
# To provide additional ipoib interfaces to containers use SR-IOV and pipework
# to assign them.
[ $IFTYPE = ipoib ] && {
GUEST_IFNAME=$CONTAINER_IFNAME
}
# If it's a physical interface, create a macvlan subinterface
[ "$IFTYPE" = phys ] && {
[ "$VLAN" ] && {
[ ! -d "/sys/class/net/${IFNAME}.${VLAN}" ] && {
ip link add link "$IFNAME" name "$IFNAME.$VLAN" mtu "$MTU" type vlan id "$VLAN"
[ $IFTYPE = phys ] && {
[ "$VLAN" ] && {
[ ! -d /sys/class/net/$IFNAME.$VLAN ] && {
ip link add link $IFNAME name $IFNAME.$VLAN mtu $MTU type vlan id $VLAN
}
ip link set $IFNAME up
IFNAME=$IFNAME.$VLAN
}
ip link set "$IFNAME" up
IFNAME=$IFNAME.$VLAN
}
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
ip link add link "$IFNAME" dev "$GUEST_IFNAME" mtu "$MTU" type macvlan mode bridge
ip link set "$IFNAME" up
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
ip link add link $IFNAME dev $GUEST_IFNAME mtu $MTU type macvlan mode bridge
ip link set $IFNAME up
}
# If it's an IPoIB interface, create a virtual IPoIB interface (the IPoIB
# equivalent of a macvlan device)
#
# Note: no macvlan subinterface nor Ethernet bridge can be created on top of an
# IPoIB interface. InfiniBand is not Ethernet. IPoIB is an IP layer on top of
# InfiniBand, without an intermediate Ethernet layer.
[ "$IFTYPE" = ipoib ] && {
GUEST_IFNAME="${IFNAME}.${NSPID}"
# If a partition key is provided, use it
[ "$PKEY" ] && {
GUEST_IFNAME="${IFNAME}.${PKEY}.${NSPID}"
PKEY="pkey 0x$PKEY"
}
ip link add link "$IFNAME" name "$GUEST_IFNAME" type ipoib $PKEY
ip link set "$IFNAME" up
}
# If its a dummy interface, create a dummy interface.
[ "$IFTYPE" = dummy ] && {
GUEST_IFNAME=du$NSPID$CONTAINER_IFNAME
ip link add dev "$GUEST_IFNAME" type dummy
}
# If the `route` command was specified ...
if [ "$IFTYPE" = route ]; then
# ... discard the first two arguments and pass the rest to the route command.
shift 2
ip netns exec "$NSPID" ip route "$@"
else
# Otherwise, run normally.
ip link set "$GUEST_IFNAME" netns "$NSPID"
ip netns exec "$NSPID" ip link set "$GUEST_IFNAME" name "$CONTAINER_IFNAME"
[ "$MACADDR" ] && ip netns exec "$NSPID" ip link set dev "$CONTAINER_IFNAME" address "$MACADDR"
# When using any of the DHCP methods, we start a DHCP client in the
# network namespace of the container. With the 'dhcp' method, the
# client used is taken from the Docker busybox image (therefore
# requiring no specific client installed on the host). Other methods
# use a locally installed client.
case "$DHCP_CLIENT" in
dhcp)
docker run -d --net container:$GUESTNAME --cap-add NET_ADMIN \
busybox udhcpc -i "$CONTAINER_IFNAME" -x "hostname:$GUESTNAME" \
$DHCP_OPTIONS \
>/dev/null
;;
udhcpc)
DHCP_Q="-q"
[ "$DHCP_FOREGROUND" ] && {
DHCP_OPTIONS="$DHCP_OPTIONS -f"
}
ip netns exec "$NSPID" "$DHCP_CLIENT" -qi "$CONTAINER_IFNAME" \
-x "hostname:$GUESTNAME" \
-p "/var/run/udhcpc.$GUESTNAME.pid" \
$DHCP_OPTIONS
[ ! "$DHCP_FOREGROUND" ] && {
rm "/var/run/udhcpc.$GUESTNAME.pid"
}
;;
dhclient)
ip netns exec "$NSPID" "$DHCP_CLIENT" "$CONTAINER_IFNAME" \
-pf "/var/run/dhclient.$GUESTNAME.pid" \
-lf "/etc/dhclient/dhclient.$GUESTNAME.leases" \
$DHCP_OPTIONS
# kill dhclient after get ip address to prevent device be used after container close
[ ! "$DHCP_FOREGROUND" ] && {
kill "$(cat "/var/run/dhclient.$GUESTNAME.pid")"
rm "/var/run/dhclient.$GUESTNAME.pid"
}
;;
dhcpcd)
ip netns exec "$NSPID" "$DHCP_CLIENT" -q "$CONTAINER_IFNAME" -h "$GUESTNAME"
;;
"")
if installed ipcalc; then
eval "$(ipcalc -b $IPADDR)"
ip netns exec "$NSPID" ip addr add "$IPADDR" brd "$BROADCAST" dev "$CONTAINER_IFNAME"
else
ip netns exec "$NSPID" ip addr add "$IPADDR" dev "$CONTAINER_IFNAME"
fi
[ "$GATEWAY" ] && {
ip netns exec "$NSPID" ip route delete default >/dev/null 2>&1 && true
}
ip netns exec "$NSPID" ip link set "$CONTAINER_IFNAME" up
[ "$GATEWAY" ] && {
ip netns exec "$NSPID" ip route get "$GATEWAY" >/dev/null 2>&1 || \
ip netns exec "$NSPID" ip route add "$GATEWAY/32" dev "$CONTAINER_IFNAME"
ip netns exec "$NSPID" ip route replace default via "$GATEWAY"
}
;;
esac
# Give our ARP neighbors a nudge about the new interface
if installed arping; then
IPADDR=$(echo "$IPADDR" | cut -d/ -f1)
ip netns exec "$NSPID" arping -c 1 -A -I "$CONTAINER_IFNAME" "$IPADDR" > /dev/null 2>&1 || true
else
echo "Warning: arping not found; interface may not be immediately reachable"
fi
ip link set $GUEST_IFNAME netns $NSPID
ip netns exec $NSPID ip link set $GUEST_IFNAME name $CONTAINER_IFNAME
[ "$MACADDR" ] && ip netns exec $NSPID ip link set dev $CONTAINER_IFNAME address $MACADDR
if [ "$IPADDR" = "dhcp" ]
then
[ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME -x hostname:$GUESTNAME
if [ $DHCP_CLIENT = "dhclient" ]
then
# kill dhclient after get ip address to prevent device be used after container close
ip netns exec $NSPID $DHCP_CLIENT -pf "/var/run/dhclient.$NSPID.pid" $CONTAINER_IFNAME
kill "$(cat "/var/run/dhclient.$NSPID.pid")"
rm "/var/run/dhclient.$NSPID.pid"
fi
[ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME -h $GUESTNAME
else
ip netns exec $NSPID ip addr add $IPADDR dev $CONTAINER_IFNAME
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route delete default >/dev/null 2>&1 && true
}
ip netns exec $NSPID ip link set $CONTAINER_IFNAME up
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route get $GATEWAY >/dev/null 2>&1 || \
ip netns exec $NSPID ip route add $GATEWAY/32 dev $CONTAINER_IFNAME
ip netns exec $NSPID ip route replace default via $GATEWAY
}
fi
# Remove NSPID to avoid `ip netns` catch it.
rm -f "/var/run/netns/$NSPID"
# vim: set tabstop=2 shiftwidth=2 softtabstop=2 expandtab :
# Give our ARP neighbors a nudge about the new interface
if which arping > /dev/null 2>&1
then
IPADDR=$(echo $IPADDR | cut -d/ -f1)
ip netns exec $NSPID arping -c 1 -A -I $CONTAINER_IFNAME $IPADDR > /dev/null 2>&1 || true
else
echo "Warning: arping not found; interface may not be immediately reachable"
fi
# Remove NSPID to avoid `ip netns` catch it.
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID
exit 0

4
spec/Vagrantfile vendored
View file

@ -15,6 +15,10 @@ Vagrant.configure("2") do |config|
config.cache.auto_detect = true
config.vm.provider :lxc do |lxc|
# lxc.sudo_wrapper = '/usr/bin/lxc-vagrant-wrapper'
end
config.vm.provision :shell,
inline: 'mkdir -p /vagrant/tmp && echo -n "Provisioned" > /vagrant/tmp/provisioning'

View file

@ -24,7 +24,6 @@ describe Vagrant::LXC::Action::ForwardPorts do
machine.stub_chain(:config, :vm, :networks).and_return(networks)
machine.stub(provider: provider, data_dir: data_dir)
subject.stub(redir_version: 3)
subject.stub(exec: true)
subject.stub(spawn: pid)
end
@ -35,25 +34,25 @@ describe Vagrant::LXC::Action::ForwardPorts do
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n #{host_ip}:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"redir --laddr=#{host_ip} --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 as default if host_ip is nil' do
it 'skips --laddr parameter if host_ip is nil' do
forward_conf.delete(:host_ip)
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"redir --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is a blank string' do
it 'skips --laddr parameter if host_ip is a blank string' do
forward_conf[:host_ip] = ' '
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"redir --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
@ -71,15 +70,6 @@ describe Vagrant::LXC::Action::ForwardPorts do
expect(subject).not_to have_received(:spawn)
end
it 'uses redir 2.x command line interface' do
subject.stub(system: true)
subject.stub(redir_version: 2)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir --laddr=#{host_ip} --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'raises RedirNotInstalled error if `redir` is not installed' do
subject.stub(system: false)
expect { subject.call(env) }.to raise_error(Vagrant::LXC::Errors::RedirNotInstalled)
@ -92,25 +82,25 @@ describe Vagrant::LXC::Action::ForwardPorts do
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n #{host_ip}:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"sudo redir --laddr=#{host_ip} --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is nil' do
it 'skips --laddr parameter if host_ip is nil' do
forward_conf.delete(:host_ip)
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"sudo redir --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is a blank string' do
it 'skips --laddr parameter if host_ip is a blank string' do
forward_conf[:host_ip] = ' '
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
"sudo redir --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
end

View file

@ -30,7 +30,16 @@ describe Vagrant::LXC::Driver::CLI do
describe 'version' do
before do
allow(subject).to receive(:run).with(:create, '--version').and_return(lxc_version_out)
allow(subject).to receive(:support_version_command?).and_return(true)
allow(subject).to receive(:run).with(:version).and_return(lxc_version_out)
end
describe 'lxc version before 1.x.x' do
let(:lxc_version_out) { "lxc version: 0.x.y-rc1\n" }
it 'parses the version from the output' do
expect(subject.version).to eq('0.x.y-rc1')
end
end
describe 'lxc version after 1.x.x' do
@ -44,11 +53,24 @@ describe Vagrant::LXC::Driver::CLI do
describe 'config' do
before do
allow(subject).to receive(:support_version_command?).and_return(support_version_command?)
allow(subject).to receive(:run).with(:config, 'lxc.lxcpath').and_return(lxc_config_out)
allow(subject).to receive(:run).with(:version).and_return(lxc_version_out)
allow(subject).to receive(:run).with(:create, '--version').and_return(lxc_version_out)
end
describe 'lxc version after 1.x.x'do
describe 'lxc version before 1.x.x' do
let(:support_version_command?) { true }
let(:lxc_config_out) { "/var/lib/lxc\n" }
let(:lxc_version_out) { "lxc version: 0.x.y-rc1\n" }
it 'not supported' do
expect{subject.config('lxc.lxcpath')}.to raise_error(Vagrant::LXC::Errors::CommandNotSupported)
end
end
describe 'lxc version before after 1.x.x'do
let(:support_version_command?) { false }
let(:lxc_config_out) { "/var/lib/lxc\n" }
let(:lxc_version_out) { "1.0.0\n" }
@ -134,11 +156,36 @@ describe Vagrant::LXC::Driver::CLI do
before do
allow(subject).to receive(:run)
subject.stop
end
it 'issues a lxc-stop with provided container name' do
expect(subject).to have_received(:run).with(:stop, '--name', name)
context 'lxc-attach is supported' do
before do
subject.stub(attach: true, supports_attach?: true)
subject.stop
end
it 'runs a /sbin/halt within the container' do
expect(subject).to have_received(:attach).with('/sbin/halt')
end
it 'issues a lxc-stop with provided container name' do
expect(subject).to have_received(:run).with(:stop, '--name', name)
end
end
context 'lxc-attach is not supported' do
before do
subject.stub(attach: false, supports_attach?: false)
subject.stop
end
it 'runs a /sbin/halt within the container' do
expect(subject).to_not have_received(:attach)
end
it 'issues a lxc-stop with provided container name' do
expect(subject).to have_received(:run).with(:stop, '--name', name)
end
end
end
@ -185,6 +232,13 @@ describe Vagrant::LXC::Driver::CLI do
subject.attach *(command + [{namespaces: ['network', 'mount']}])
expect(subject).to have_received(:run).with(:attach, '--name', name, '--namespaces', 'NETWORK|MOUNT', '--', *command)
end
it 'raises a NamespacesNotSupported error if not supported' do
allow(subject).to receive(:run).with(:attach, '-h', :show_stderr => true).and_return({:stdout => '', :stderr => 'not supported'})
expect {
subject.attach *(command + [{namespaces: ['network', 'mount']}])
}.to raise_error(Vagrant::LXC::Errors::NamespacesNotSupported)
end
end
describe 'transition block' do
@ -206,4 +260,33 @@ describe Vagrant::LXC::Driver::CLI do
skip 'waits for the expected container state'
end
describe 'check for whether lxc-attach is supported' do
let(:name) { 'a-running-container' }
subject { described_class.new(sudo_wrapper, name) }
context 'lxc-attach is present on system' do
before { subject.stub(run: true) }
it 'returns true if `lxc-attach --name CNAME -- /bin/true` works' do
expect(subject.supports_attach?).to be_truthy
expect(subject).to have_received(:run).with(
:attach, '--name', name, '--', '/bin/true'
)
end
end
context 'lxc-attach is not present on system' do
before do
allow(subject).to receive(:run).and_raise(Vagrant::LXC::Errors::ExecuteError.new('msg'))
end
it 'returns true if `lxc-attach --name CNAME -- /bin/true` works' do
expect(subject.supports_attach?).to be_falsy
expect(subject).to have_received(:run).with(
:attach, '--name', name, '--', '/bin/true'
)
end
end
end
end

View file

@ -54,7 +54,7 @@ describe Vagrant::LXC::Driver do
it 'creates container with the right arguments' do
expect(cli).to have_received(:create).with(
template_path,
template_name,
backingstore,
backingstore_opts,
config_file,
@ -75,10 +75,21 @@ describe Vagrant::LXC::Driver do
end
end
describe 'supports_attach?' do
let(:cli) { double(Vagrant::LXC::Driver::CLI, supports_attach?: true) }
subject { described_class.new('name', nil, cli) }
it 'delegates to cli object' do
expect(subject.supports_attach?).to be_truthy
expect(cli).to have_received(:supports_attach?)
end
end
describe 'start' do
let(:customizations) { [['a', '1'], ['b', '2']] }
let(:internal_customization) { ['internal', 'customization'] }
let(:cli) { double(Vagrant::LXC::Driver::CLI, start: true) }
let(:cli) { double(Vagrant::LXC::Driver::CLI, start: true, support_config_command?: false) }
let(:sudo) { double(Vagrant::LXC::SudoWrapper) }
subject { described_class.new('name', sudo, cli) }
@ -86,9 +97,8 @@ describe Vagrant::LXC::Driver do
before do
sudo.should_receive(:run).with('cat', '/var/lib/lxc/name/config').exactly(2).times.
and_return('# CONFIGURATION')
sudo.should_receive(:run).twice.with('cp', '-f', %r{/(run|tmp)/.*}, '/var/lib/lxc/name/config')
sudo.should_receive(:run).twice.with('cp', '-f', %r{/tmp/.*}, '/var/lib/lxc/name/config')
sudo.should_receive(:run).twice.with('chown', 'root:root', '/var/lib/lxc/name/config')
expect(cli).to receive(:config).with("lxc.lxcpath").and_return("/var/lib/lxc")
subject.customizations << internal_customization
subject.start(customizations)
@ -142,11 +152,21 @@ describe Vagrant::LXC::Driver do
end
describe 'containers_path' do
let(:cli) { double(Vagrant::LXC::Driver::CLI, config: cli_config_value) }
let(:cli) { double(Vagrant::LXC::Driver::CLI, config: cli_config_value, support_config_command?: cli_support_config_command_value) }
subject { described_class.new('name', nil, cli) }
describe 'lxc version before 1.x.x' do
let(:cli_support_config_command_value) { false }
let(:cli_config_value) { '/var/lib/lxc' }
it 'delegates to cli' do
expect(subject.containers_path).to eq(cli_config_value)
end
end
describe 'lxc version after 1.x.x' do
let(:cli_support_config_command_value) { true }
let(:cli_config_value) { '/etc/lxc' }
it 'delegates to cli' do
@ -172,10 +192,14 @@ describe Vagrant::LXC::Driver do
subject.share_folders(folders)
end
it "creates guest folder under container's rootfs" do
expect(sudo_wrapper).to have_received(:run).with("mkdir", "-p", "#{rootfs_path}/#{expected_guest_path}")
end
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind 0 0"
]
end
@ -189,7 +213,7 @@ describe Vagrant::LXC::Driver do
it 'supports directories with spaces' do
expect(subject.customizations).to include [
'mount.entry',
"/path/with\\040space tmp/with\\040space none bind,create=dir 0 0"
"/path/with\\040space tmp/with\\040space none bind 0 0"
]
end
end
@ -200,9 +224,9 @@ describe Vagrant::LXC::Driver do
# Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4
lxc.pty.max = 1024
lxc.rootfs.path = #{rootfs_path}
lxc.tty = 4
lxc.pts = 1024
lxc.rootfs = #{rootfs_path}
# VAGRANT-BEGIN
lxc.network.type=veth
lxc.network.name=eth1
@ -219,7 +243,7 @@ describe Vagrant::LXC::Driver do
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind 0 0"
]
end
end
@ -230,9 +254,9 @@ describe Vagrant::LXC::Driver do
# Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4
lxc.pty.max = 1024
lxc.rootfs.path = overlayfs:/path/to/master/directory:#{rootfs_path}
lxc.tty = 4
lxc.pts = 1024
lxc.rootfs = overlayfs:/path/to/master/directory:#{rootfs_path}
# VAGRANT-BEGIN
lxc.network.type=veth
lxc.network.name=eth1
@ -249,7 +273,7 @@ describe Vagrant::LXC::Driver do
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind 0 0"
]
end
end

View file

@ -1,4 +1,4 @@
#!<%= cmd_paths['ruby'] %>
#!/opt/vagrant/embedded/bin/ruby
# Automatically created by vagrant-lxc
class Whitelist
@ -78,8 +78,9 @@ class Whitelist
end
end
base = "<%= lxc_base_path %>"
base = "/var/lib/lxc"
base_path = %r{\A#{base}/.*\z}
templates_path = %r{\A/usr/(share|lib|lib64|local/lib)/lxc/templates/.*\z}
##
# Commands from provider.rb
@ -95,13 +96,18 @@ Whitelist.add '<%= cmd_paths['mkdir'] %>', '-p', base_path
# - Container config customizations and pruning
Whitelist.add '<%= cmd_paths['cp'] %>', '-f', %r{/tmp/.*}, base_path
Whitelist.add '<%= cmd_paths['chown'] %>', 'root:root', base_path
# - Template import
Whitelist.add '<%= cmd_paths['cp'] %>', %r{\A.*\z}, templates_path
Whitelist.add '<%= cmd_paths['chmod'] %>', '+x', templates_path
# - Template removal
Whitelist.add '<%= cmd_paths['rm'] %>', templates_path
# - Packaging
Whitelist.add '<%= cmd_paths['tar'] %>', '--numeric-owner', '-cvzf', %r{/tmp/.*/rootfs.tar.gz}, '-C', base_path, './rootfs'
Whitelist.add '<%= cmd_paths['chown'] %>', /\A\d+:\d+\z/, %r{\A/tmp/.*/rootfs\.tar\.gz\z}
# - Private network script and commands
Whitelist.add '<%= cmd_paths['ip'] %>', 'addr', 'add', /(\d+|\.)+\/24/, 'dev', /.+/
Whitelist.add '<%= cmd_paths['ip'] %>', 'link', 'set', /.+/, /(up|down)/
Whitelist.add '<%= cmd_paths['brctl'] %>', /(addbr|delbr)/, /.+/
Whitelist.add '<%= cmd_paths['ifconfig'] %>', /.+/, 'down'
Whitelist.add '<%= cmd_paths['brctl'] %>', 'delbr', /.+/
Whitelist.add_regex %r{<%= pipework_regex %>}, '**'
##
@ -109,7 +115,6 @@ Whitelist.add_regex %r{<%= pipework_regex %>}, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-version'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-ls'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-info', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-info', '--name', /.*/, '-iH'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-create', '-B', /.*/, '--template', /.*/, '--name', /.*/, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-create', '--version'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-destroy', '--name', /.*/
@ -119,7 +124,6 @@ Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-shutdown', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '--name', /.*/, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '-h'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-config', 'lxc.lxcpath'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-update-config', '-c', /.*/
##
# Commands from driver/action/remove_temporary_files.rb