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 /tags
/gems.tags /gems.tags
/Gemfile.lock
.vagrant .vagrant
/cache /cache

View file

@ -1,10 +1,8 @@
language: ruby language: ruby
rvm: rvm:
- 2.2 - 2.0.0
- 2.3 - 2.1.1
- 2.4 matrix:
- 2.5 allow_failures:
install: - rvm: 2.1.1
- gem install -v 1.12.5 bundler
- bundle _1.12.5_ install --jobs=3 --retry=3
script: "bundle exec rake ci" 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) ## [1.1.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.1...1.1.0) (Jan 14, 2015)
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)
BACKWARDS INCOMPATIBILITIES: BACKWARDS INCOMPATIBILITIES:

View file

@ -1,16 +1,16 @@
source 'https://rubygems.org' source 'https://rubygems.org'
group :development do 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'
gem 'guard-rspec' gem 'guard-rspec'
gem 'rb-inotify' gem 'rb-inotify'
end end
group :development, :test do group :development, :test do
gem 'rake', '~> 10.4.2' gem 'rake'
gem 'rspec', '~> 3.5.0' gem 'rspec', '2.99.0'
gem 'coveralls', '~> 0.7.2', require: (ENV['COVERAGE'] == 'true') gem 'coveralls', require: (ENV['COVERAGE'] == 'true')
gem 'vagrant-spec', git: 'https://github.com/mitchellh/vagrant-spec.git' gem 'vagrant-spec', git: 'https://github.com/mitchellh/vagrant-spec.git'
end 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 # 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 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 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/) [this blog post](http://fabiorehm.com/blog/2013/04/28/lxc-provider-for-vagrant/)
to see it in action. to see it in action.
## Features ## Features
* Provides the same workflow as the Vagrant VirtualBox provider * 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) * Private networking via [`pipework`](https://github.com/jpetazzo/pipework)
## Requirements ## Requirements
* [Vagrant 1.9+](http://www.vagrantup.com/downloads.html) * [Vagrant 1.5+](http://www.vagrantup.com/downloads.html) (tested with 1.7.2)
* lxc >=2.1 * lxc 0.7.5+
* `redir` (if you are planning to use port forwarding) * `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`) * `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+ 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 hosts and installing the dependencies on it basically means a `apt-get install lxc lxc-templates cgroup-lite redir`
`apt-get install lxc lxc-templates cgroup-lite redir`. For setting up other (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). 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) 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
Base boxes provided on Atlas haven't been refreshed for a good while and shouldn't be relied on. Base boxes can be found on [Atlas](https://atlas.hashicorp.com/boxes/search?provider=lxc)
Your best best is to build your boxes yourself. Some scripts to build your own are available at and some scripts to build your own are available at [fgrehm/vagrant-lxc-base-boxes](https://github.com/fgrehm/vagrant-lxc-base-boxes).
[hsoft/vagrant-lxc-base-boxes](https://github.com/hsoft/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) 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. 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). 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 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) [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 ```ruby
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc| config.vm.provider :lxc do |lxc|
lxc.backingstore = 'lvm' # or 'btrfs', 'overlayfs', ... lxc.backingstore = 'lvm' # or 'btrfs',...
# lvm specific options # lvm specific options
lxc.backingstore_option '--vgname', 'schroots' lxc.backingstore_option '--vgname', 'schroots'
lxc.backingstore_option '--fssize', '5G' lxc.backingstore_option '--fssize', '5G'
@ -142,33 +140,31 @@ Vagrant.configure("2") do |config|
end end
``` ```
## Unprivileged containers support 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
Since v1.4.0, `vagrant-lxc` gained support for unprivileged containers. For now, since it's a new required and a default for all Vagrant environments can be set from your
feature, privileged containers are still the default, but you can have your `Vagrantfile` use `~/.vagrant.d/Vagrantfile` using the same `provider` block:
unprivileged containers with the `privileged` flag (which defaults to `true`). Example:
```ruby ```ruby
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc| config.vm.provider :lxc do |lxc|
lxc.privileged = false lxc.backingstore = 'none'
end end
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 ## Avoiding `sudo` passwords
If you're not using unprivileged containers, this plugin requires **a lot** of `sudo`ing To work This plugin requires **a lot** of `sudo`ing since [user namespaces](https://wiki.ubuntu.com/UserNamespace)
around that, you can use the `vagrant lxc sudoers` command which will create a file under is not supported yet. To work around that, you can use the `vagrant lxc sudoers`
`/etc/sudoers.d/vagrant-lxc` whitelisting all commands required by `vagrant-lxc` to run. 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 If you are interested on what will be generated by that command, please check
[this code](lib/vagrant-lxc/command/sudoers.rb). [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 ## More information

View file

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

View file

@ -4,7 +4,8 @@ require 'vagrant-lxc/action/create'
require 'vagrant-lxc/action/destroy' require 'vagrant-lxc/action/destroy'
require 'vagrant-lxc/action/destroy_confirm' require 'vagrant-lxc/action/destroy_confirm'
require 'vagrant-lxc/action/compress_rootfs' 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/forced_halt'
require 'vagrant-lxc/action/forward_ports' require 'vagrant-lxc/action/forward_ports'
require 'vagrant-lxc/action/gc_private_network_bridges' 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_settings'
require 'vagrant-lxc/action/prepare_nfs_valid_ids' require 'vagrant-lxc/action/prepare_nfs_valid_ids'
require 'vagrant-lxc/action/private_networks' require 'vagrant-lxc/action/private_networks'
require 'vagrant-lxc/action/remove_temporary_files'
require 'vagrant-lxc/action/setup_package_files' require 'vagrant-lxc/action/setup_package_files'
require 'vagrant-lxc/action/warn_networks' require 'vagrant-lxc/action/warn_networks'
@ -124,6 +126,7 @@ module Vagrant
end end
b2.use ClearForwardedPorts b2.use ClearForwardedPorts
b2.use RemoveTemporaryFiles
b2.use GcPrivateNetworkBridges b2.use GcPrivateNetworkBridges
b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3| b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result] if !env2[:result]
@ -181,7 +184,8 @@ module Vagrant
def self.action_ssh_ip def self.action_ssh_ip
Builder.new.tap do |b| Builder.new.tap do |b|
b.use Builtin::Call, Builtin::ConfigValidate do |env, b2| 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 end
end end

View file

@ -8,15 +8,11 @@ module Vagrant
def call(env) def call(env)
@env = env @env = env
driver = env[:machine].provider.driver
config = env[:machine].provider_config config = env[:machine].provider_config
utsname = env[:machine].config.vm.hostname || env[:machine].id utsname = env[:machine].config.vm.hostname || env[:machine].id
if driver.supports_new_config_format config.customize 'utsname', utsname
config.customize 'uts.name', utsname
else
config.customize 'utsname', utsname
end
# Fix apparmor issues when starting Ubuntu 14.04 containers # Fix apparmor issues when starting Ubuntu 14.04 containers
# See https://github.com/fgrehm/vagrant-lxc/issues/278 for more information # 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' config.customize 'mount.entry', '/sys/fs/pstore sys/fs/pstore none bind,optional 0 0'
end 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") env[:ui].info I18n.t("vagrant_lxc.messages.starting")
driver.start(config.customizations) env[:machine].provider.driver.start(config.customizations)
@app.call env @app.call env
end end

View file

@ -19,28 +19,14 @@ module Vagrant
container_name = generate_container_name(env) container_name = generate_container_name(env)
end end
backingstore = config.backingstore env[:machine].provider.driver.create(
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(
container_name, container_name,
backingstore, config.backingstore,
config.backingstore_options, config.backingstore_options,
env[:lxc_template_src], env[:lxc_template_src],
env[:lxc_template_config], env[:lxc_template_config],
template_options env[:lxc_template_opts]
) )
driver.update_config_keys
env[:machine].id = container_name 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 Vagrant
module LXC module LXC
module Action module Action
class FetchIpWithLxcInfo class FetchIpWithLxcAttach
# Include this so we can use `Subprocess` more easily. # Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable include Vagrant::Util::Retryable
def initialize(app, env) def initialize(app, env)
@app = app @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 end
def call(env) def call(env)
env[:machine_ip] ||= assigned_ip(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 ensure
@app.call(env) @app.call(env)
end end
def assigned_ip(env) def assigned_ip(env)
config = env[:machine].provider_config
fetch_ip_tries = config.fetch_ip_tries
driver = env[:machine].provider.driver driver = env[:machine].provider.driver
ip = '' ip = ''
return config.ssh_ip_addr if not config.ssh_ip_addr.nil? retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
retryable(:on => LXC::Errors::ExecuteError, :tries => fetch_ip_tries, :sleep => 3) do
unless ip = get_container_ip_from_ip_addr(driver) unless ip = get_container_ip_from_ip_addr(driver)
# retry # retry
raise LXC::Errors::ExecuteError, :command => "lxc-info" raise LXC::Errors::ExecuteError, :command => "lxc-attach"
end end
end end
ip ip
@ -33,8 +32,8 @@ module Vagrant
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385 # From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
def get_container_ip_from_ip_addr(driver) def get_container_ip_from_ip_addr(driver)
output = driver.info '-iH' output = driver.attach '/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'eth0', namespaces: ['network', 'mount']
if output =~ /^([0-9.]+)/ if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
return $1.to_s return $1.to_s
end end
end end

View file

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

View file

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

View file

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

View file

@ -26,19 +26,16 @@ module Vagrant
next if type.to_sym != :private_network next if type.to_sym != :private_network
container_name = env[:machine].provider.driver.container_name container_name = env[:machine].provider.driver.container_name
address_type = config[:type]
ip = config[:ip] ip = config[:ip]
bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) } bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) }
bridge = config.fetch(:lxc__bridge_name) 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
end end
def build_bridge_ip(ip) def build_bridge_ip(ip)
if ip ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
end
end end
end 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 'tempfile'
require "vagrant-lxc/driver"
require "vagrant-lxc/sudo_wrapper"
module Vagrant module Vagrant
module LXC module LXC
module Command module Command
@ -28,7 +25,7 @@ module Vagrant
argv = parse_options(opts) argv = parse_options(opts)
return unless argv return unless argv
wrapper_path = SudoWrapper.dest_path wrapper_path = Vagrant::LXC.sudo_wrapper_path
wrapper = create_wrapper! wrapper = create_wrapper!
sudoers = create_sudoers!(options[:user], wrapper_path) 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 # This requires vagrant 1.5.2+ https://github.com/mitchellh/vagrant/commit/3371c3716278071680af9b526ba19235c79c64cb
def create_wrapper! def create_wrapper!
lxc_base_path = Driver.new("").containers_path
wrapper = Tempfile.new('lxc-wrapper').tap do |file| wrapper = Tempfile.new('lxc-wrapper').tap do |file|
template = Vagrant::Util::TemplateRenderer.new( template = Vagrant::Util::TemplateRenderer.new(
'sudoers.rb', 'sudoers.rb',
:template_root => Vagrant::LXC.source_root.join('templates').to_s, :template_root => Vagrant::LXC.source_root.join('templates').to_s,
:cmd_paths => build_cmd_paths_hash, :cmd_paths => build_cmd_paths_hash,
:lxc_base_path => lxc_base_path, :pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/gems/vagrant-lxc.+/scripts/pipework"
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/(?:\\d+?\\.\\d+?\\.\\d+?/)?gems/vagrant-lxc.+/scripts/pipework"
) )
file.puts template.render file.puts template.render
end end
@ -85,10 +80,9 @@ module Vagrant
def build_cmd_paths_hash def build_cmd_paths_hash
{}.tap do |hash| {}.tap do |hash|
%w( which cat mkdir cp chown chmod rm tar chown ip ifconfig brctl ).each do |cmd| %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 end
hash['lxc_bin'] = Pathname(`sudo which lxc-create`.strip).parent.to_s hash['lxc_bin'] = Pathname(`which lxc-create`.strip).parent.to_s
hash['ruby'] = Gem.ruby
end end
end end
end end

View file

@ -18,29 +18,12 @@ module Vagrant
# machine name, set this to :machine # machine name, set this to :machine
attr_accessor :container_name 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 def initialize
@customizations = [] @customizations = []
@backingstore = UNSET_VALUE @backingstore = UNSET_VALUE
@backingstore_options = [] @backingstore_options = []
@sudo_wrapper = UNSET_VALUE
@container_name = 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 end
# Customize the container by calling `lxc-start` with the given # Customize the container by calling `lxc-start` with the given
@ -64,13 +47,10 @@ module Vagrant
end end
def finalize! def finalize!
@sudo_wrapper = nil if @sudo_wrapper == UNSET_VALUE
@container_name = nil if @container_name == 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 @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 end
end end

View file

@ -3,7 +3,6 @@ require "vagrant/util/subprocess"
require "vagrant-lxc/errors" require "vagrant-lxc/errors"
require "vagrant-lxc/driver/cli" require "vagrant-lxc/driver/cli"
require "vagrant-lxc/sudo_wrapper"
require "etc" require "etc"
@ -17,13 +16,15 @@ module Vagrant
class ContainerNotFound < StandardError; end class ContainerNotFound < StandardError; end
# Default root folder where container configs are stored # Default root folder where container configs are stored
DEFAULT_CONTAINERS_PATH = '/var/lib/lxc'
attr_reader :container_name, attr_reader :container_name,
:customizations :customizations
def initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true) def initialize(container_name, sudo_wrapper, cli = nil)
@container_name = container_name @container_name = container_name
@sudo_wrapper = sudo_wrapper || SudoWrapper.new(privileged: privileged) @sudo_wrapper = sudo_wrapper
@cli = cli || CLI.new(@sudo_wrapper, container_name) @cli = cli || CLI.new(sudo_wrapper, container_name)
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver") @logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
@customizations = [] @customizations = []
end end
@ -34,7 +35,7 @@ module Vagrant
# Root folder where container configs are stored # Root folder where container configs are stored
def containers_path def containers_path
@containers_path ||= @cli.config('lxc.lxcpath') @containers_path ||= @cli.support_config_command? ? @cli.config('lxc.lxcpath') : DEFAULT_CONTAINERS_PATH
end end
def all_containers def all_containers
@ -45,25 +46,21 @@ module Vagrant
Pathname.new("#{containers_path}/#{@container_name}") Pathname.new("#{containers_path}/#{@container_name}")
end end
def config_path
base_path.join('config').to_s
end
def rootfs_path def rootfs_path
pathtype, path = config_string.match(/^lxc\.rootfs(?:\.path)?\s+=\s+(.+:)?(.+)$/)[1..2] config_entry = config_string.match(/^lxc\.rootfs\s+=\s+(.+)$/)[1]
case pathtype case config_entry
when 'overlayfs:' when /^overlayfs:/
# Split on colon (:), ignoring any colon escaped by an escape character ( \ ) # Split on colon (:), ignoring any colon escaped by an escape character ( \ )
# Pays attention to when the escape character is itself escaped. # 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 if overlay_path
Pathname.new(overlay_path) Pathname.new(overlay_path)
else else
# Malformed: fall back to prior behaviour # Malformed: fall back to prior behaviour
Pathname.new(path) Pathname.new(config_entry)
end end
else else
Pathname.new(path) Pathname.new(config_entry)
end end
end end
@ -76,27 +73,40 @@ module Vagrant
end end
def config_string def config_string
@sudo_wrapper.run('cat', config_path) @sudo_wrapper.run('cat', base_path.join('config').to_s)
end end
def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {}) def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {})
@cli.name = @container_name = name @cli.name = @container_name = name
@logger.debug "Creating container..." import_template(template_path) do |template_name|
@logger.debug "Creating container..."
@cli.create template_path, backingstore, backingstore_options, config_file, template_options @cli.create template_name, backingstore, backingstore_options, config_file, template_options
end
end end
def share_folders(folders) def share_folders(folders)
folders.each do |f| 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
end end
def share_folder(host_path, guest_path, mount_options = nil) def share_folder(host_path, guest_path, mount_options = nil)
guest_path = guest_path.gsub(/^\//, '').gsub(' ', '\\\040') guest_path = guest_path.gsub(/^\//, '')
mount_options = Array(mount_options || ['bind', 'create=dir']) 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') 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"] @customizations << ['mount.entry', "#{host_path} #{guest_path} none #{mount_options.join(',')} 0 0"]
end end
@ -122,38 +132,25 @@ module Vagrant
@cli.destroy @cli.destroy
end end
def supports_attach?
@cli.supports_attach?
end
def attach(*command) def attach(*command)
@cli.attach(*command) @cli.attach(*command)
end end
def info(*command) def configure_private_network(bridge_name, bridge_ip, container_name, ip)
@cli.info(*command)
end
def configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip)
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}" @logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
if ip cmd = [
ip += '/24' Vagrant::LXC.source_root.join('scripts/pipework').to_s,
end bridge_name,
container_name,
if ! bridge_exists?(bridge_name) "#{ip}/24"
if not bridge_ip ]
raise "Bridge is missing and no IP was specified!" @sudo_wrapper.run(*cmd)
end
@logger.info "Creating the bridge #{bridge_name}"
cmd = [
'brctl',
'addbr',
bridge_name
]
@sudo_wrapper.run(*cmd)
end
if ! bridge_has_an_ip?(bridge_name) 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}" @logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
cmd = [ cmd = [
'ip', 'ip',
@ -164,16 +161,7 @@ module Vagrant
bridge_name bridge_name
] ]
@sudo_wrapper.run(*cmd) @sudo_wrapper.run(*cmd)
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'up')
end end
cmd = [
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
bridge_name,
container_name,
ip ||= "dhcp"
]
@sudo_wrapper.run(*cmd)
end end
def bridge_has_an_ip?(bridge_name) 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+/ `ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
end 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) def bridge_is_in_use?(bridge_name)
# REFACTOR: This method is **VERY** hacky # REFACTOR: This method is **VERY** hacky
@logger.info "Checking if bridge #{bridge_name} is in use" @logger.info "Checking if bridge #{bridge_name} is in use"
@ -195,15 +177,12 @@ module Vagrant
end end
def remove_bridge(bridge_name) def remove_bridge(bridge_name)
if ['lxcbr0', 'virbr0'].include? bridge_name @logger.info "Checking whether bridge #{bridge_name} exists"
@logger.info "Skipping removal of system bridge #{bridge_name}" brctl_output = `ifconfig -a | grep -q #{bridge_name}`
return return if $?.to_i != 0
end
return unless bridge_exists?(bridge_name)
@logger.info "Removing bridge #{bridge_name}" @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) @sudo_wrapper.run('brctl', 'delbr', bridge_name)
end end
@ -211,10 +190,6 @@ module Vagrant
@version ||= @cli.version @version ||= @cli.version
end 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 # TODO: This needs to be reviewed and specs needs to be written
def compress_rootfs def compress_rootfs
# TODO: Pass in tmpdir so we can clean up from outside # TODO: Pass in tmpdir so we can clean up from outside
@ -245,13 +220,6 @@ module Vagrant
write_config(contents) write_config(contents)
end 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 protected
def write_customizations(customizations) def write_customizations(customizations)
@ -268,23 +236,47 @@ module Vagrant
end end
def write_config(contents) def write_config(contents)
confpath = base_path.join('config').to_s Tempfile.new('lxc-config').tap do |file|
begin file.chmod 0644
File.open(confpath, File::RDWR) do |file| file.write contents
file.write contents file.close
end @sudo_wrapper.run 'cp', '-f', file.path, base_path.join('config').to_s
rescue @sudo_wrapper.run 'chown', 'root:root', base_path.join('config').to_s
# 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
end end
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 end
end end

View file

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

View file

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

View file

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

View file

@ -2,6 +2,7 @@ require "log4r"
require "vagrant-lxc/action" require "vagrant-lxc/action"
require "vagrant-lxc/driver" require "vagrant-lxc/driver"
require "vagrant-lxc/sudo_wrapper"
module Vagrant module Vagrant
module LXC module LXC
@ -24,9 +25,18 @@ module Vagrant
machine_id_changed machine_id_changed
end 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! def ensure_lxc_installed!
begin 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 rescue Vagrant::LXC::Errors::ExecuteError
raise Errors::LxcNotInstalled raise Errors::LxcNotInstalled
end end
@ -39,7 +49,7 @@ module Vagrant
begin begin
@logger.debug("Instantiating the container for: #{id.inspect}") @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! @driver.validate!
rescue Driver::ContainerNotFound rescue Driver::ContainerNotFound
# The container doesn't exist, so we probably have a stale # The container doesn't exist, so we probably have a stale

View file

@ -6,41 +6,15 @@ module Vagrant
attr_reader :wrapper_path attr_reader :wrapper_path
def self.dest_path def initialize(wrapper_path = nil)
"/usr/local/bin/vagrant-lxc-wrapper" @wrapper_path = wrapper_path
end
def initialize(privileged: true)
@wrapper_path = Pathname.new(SudoWrapper.dest_path).exist? && SudoWrapper.dest_path || nil
@privileged = privileged
@logger = Log4r::Logger.new("vagrant::lxc::sudo_wrapper") @logger = Log4r::Logger.new("vagrant::lxc::sudo_wrapper")
end end
def run(*command) def run(*command)
options = command.last.is_a?(Hash) ? command.last : {} options = command.last.is_a?(Hash) ? command.last : {}
command.unshift @wrapper_path if @wrapper_path && !options[:no_wrapper]
# Avoid running LXC commands with a restrictive umask. execute *(['sudo'] + command)
# 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
end end
private private
@ -68,10 +42,10 @@ module Vagrant
# nicely handled by Vagrant. # nicely handled by Vagrant.
if r.exit_code != 0 if r.exit_code != 0
if @interrupted if @interrupted
raise LXC::Errors::SubprocessInterruptError, command.inspect @logger.info("Exit code != 0, but interrupted. Ignoring.")
else else
raise LXC::Errors::ExecuteError, 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 end
end end

View file

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

View file

@ -40,9 +40,6 @@ en:
preparing: Setting up mount entries for shared folders... preparing: Setting up mount entries for shared folders...
errors: errors:
lxc_interrupt_error: |-
Interrupted
lxc_execute_error: |- lxc_execute_error: |-
There was an error executing %{command} There was an error executing %{command}
@ -67,7 +64,7 @@ en:
another provider. another provider.
lxc_not_installed: |- 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: |- lxc_redir_not_installed: |-
`redir` is not installed or is not accessible on the PATH. `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 # 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 # 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_ROOTFS=
LXC_TARBALL= LXC_TARBALL=
LXC_CONFIG= LXC_CONFIG=
LXC_USE_OLDCONFIG=
LXC_STRIP_COMPONENTS=2 LXC_STRIP_COMPONENTS=2
usage() { usage() {
cat <<EOF cat <<EOF
vagrant-lxc default template vagrant-lxc default template
@ -46,7 +44,6 @@ Required arguments:
Optional arguments: Optional arguments:
[ --config ]: Configuration file to be used when building the container [ --config ]: Configuration file to be used when building the container
[ --oldconfig ]: Use pre LXC 2.1 config format
[ -h | --help ]: This help message [ -h | --help ]: This help message
LXC internal arguments (do not pass manually!): LXC internal arguments (do not pass manually!):
@ -60,7 +57,7 @@ EOF
return 0 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 if [ $? -ne 0 ]; then
usage $(basename $0) usage $(basename $0)
@ -73,7 +70,6 @@ do
case "$1" in case "$1" in
-h|--help) usage $0 && exit 0;; -h|--help) usage $0 && exit 0;;
--config) LXC_CONFIG=$2; shift 2;; --config) LXC_CONFIG=$2; shift 2;;
--oldconfig) LXC_USE_OLDCONFIG=1; shift 1;;
--tarball) LXC_TARBALL=$2; shift 2;; --tarball) LXC_TARBALL=$2; shift 2;;
--name) LXC_NAME=$2; shift 2;; --name) LXC_NAME=$2; shift 2;;
--path) LXC_PATH=$2; shift 2;; --path) LXC_PATH=$2; shift 2;;
@ -100,6 +96,11 @@ if [ -z "${LXC_PATH}" ]; then
exit 1 exit 1
fi 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 $LXC_ROOTFS exists here, it was passed in with --rootfs
if [ -z "${LXC_ROOTFS}" ]; then if [ -z "${LXC_ROOTFS}" ]; then
config=${LXC_PATH}/config config=${LXC_PATH}/config
@ -114,6 +115,7 @@ fi
# Unpack the rootfs # Unpack the rootfs
echo "Unpacking the rootfs" echo "Unpacking the rootfs"
mkdir -p /var/lock/subsys
( (
flock -x 200 flock -x 200
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@ -122,14 +124,13 @@ echo "Unpacking the rootfs"
fi fi
mkdir -p ${LXC_ROOTFS} mkdir -p ${LXC_ROOTFS}
(cd ${LXC_ROOTFS} && tar xfz ${LXC_TARBALL} --strip-components=${LXC_STRIP_COMPONENTS} --xattrs --xattrs-include=* || true) (cd ${LXC_ROOTFS} && tar xfz ${LXC_TARBALL} --strip-components=${LXC_STRIP_COMPONENTS})
if [ ! -f ${LXC_ROOTFS}/bin/true ]; then if [ $? -ne 0 ]; then
echo "Failed to extract rootfs" echo "Failed to extract rootfs"
exit 1 exit 1
fi fi
) 200>${LXC_PATH}/vagrant_lock ) 200>/var/lock/subsys/lxc
rm ${LXC_PATH}/vagrant_lock
mkdir -p ${LXC_ROOTFS}/dev/pts/ 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 cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
rm ${LXC_PATH}/config-auto rm ${LXC_PATH}/config-auto
fi fi
echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
if [ $LXC_USE_OLDCONFIG ]; then
echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
else
echo "lxc.uts.name = ${LXC_NAME}" >> ${LXC_PATH}/config
fi
## Re-add the previously removed network config ## Re-add the previously removed network config
if [ -e "${LXC_PATH}/config-network" ]; then if [ -e "${LXC_PATH}/config-network" ]; then
@ -164,13 +160,11 @@ if [ -e "${LXC_PATH}/config-network" ]; then
rm ${LXC_PATH}/config-network rm ${LXC_PATH}/config-network
fi fi
if [ -n "${LXC_CONFIG}" ]; then ## Append the defaults
## Append the defaults echo "" >> ${LXC_PATH}/config
echo "" >> ${LXC_PATH}/config echo "##############################################" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config echo "# vagrant-lxc base box specific configuration" >> ${LXC_PATH}/config
echo "# vagrant-lxc base box specific configuration" >> ${LXC_PATH}/config cat ${LXC_CONFIG} >> ${LXC_PATH}/config
cat ${LXC_CONFIG} >> ${LXC_PATH}/config
fi
# Empty section for lxc.customize calls from vagrantfile # Empty section for lxc.customize calls from vagrantfile
echo "" >> ${LXC_PATH}/config echo "" >> ${LXC_PATH}/config

View file

@ -1,12 +1,13 @@
#!/bin/sh #!/bin/bash
# This code should (try to) follow Google's Shell Style Guide
# (https://google-styleguide.googlecode.com/svn/trunk/shell.xml) # Borrowed from https://github.com/jpetazzo/pipework
set -e set -e
case "$1" in case "$1" in
--wait) --wait)
WAIT=1 WAIT=1
;; ;;
esac esac
IFNAME=$1 IFNAME=$1
@ -18,405 +19,280 @@ if [ "$2" = "-i" ]; then
shift 2 shift 2
fi fi
if [ "$2" = "-l" ]; then
LOCAL_IFNAME=$3
shift 2
fi
GUESTNAME=$2 GUESTNAME=$2
IPADDR=$3 IPADDR=$3
MACADDR=$4 MACADDR=$4
case "$MACADDR" in if echo $MACADDR | grep -q @
*@*) then
VLAN="${MACADDR#*@}" VLAN=$(echo $MACADDR | cut -d@ -f2)
VLAN="${VLAN%%@*}" MACADDR=$(echo $MACADDR | cut -d@ -f1)
MACADDR="${MACADDR%%@*}" else
;; VLAN=
*) fi
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
[ "$IPADDR" ] || [ "$WAIT" ] || { [ "$IPADDR" ] || [ "$WAIT" ] || {
echo "Syntax:" echo "Syntax:"
echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]" echo "pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]"
echo "pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> dhcp [macaddr][@vlan]" echo "pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]"
echo "pipework route <guest> <route_command>" echo "pipework --wait [-i containerinterface]"
echo "pipework --wait [-i containerinterface]" exit 1
exit 1
} }
# Succeed if the given utility is installed. Fail otherwise. # First step: determine type of first argument (bridge, physical interface...), skip if --wait set
# 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 [ -z "$WAIT" ]; then
if [ -d "/sys/class/net/$IFNAME" ] if [ -d /sys/class/net/$IFNAME ]
then then
if [ -d "/sys/class/net/$IFNAME/bridge" ]; then if [ -d /sys/class/net/$IFNAME/bridge ]
IFTYPE=bridge then
BRTYPE=linux IFTYPE=bridge
elif installed ovs-vsctl && ovs-vsctl list-br|grep -q "^${IFNAME}$"; then BRTYPE=linux
IFTYPE=bridge elif $(which ovs-vsctl >/dev/null 2>&1) && $(ovs-vsctl list-br|grep -q ^$IFNAME$)
BRTYPE=openvswitch then
elif [ "$(cat "/sys/class/net/$IFNAME/type")" -eq 32 ]; then # InfiniBand IPoIB interface type 32 IFTYPE=bridge
IFTYPE=ipoib BRTYPE=openvswitch
# The IPoIB kernel module is fussy, set device name to ib0 if not overridden elif [ $(cat /sys/class/net/$IFNAME/type) -eq 32 ]; # Infiniband IPoIB interface type 32
CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0} then
PKEY=$VLAN IFTYPE=ipoib
else IFTYPE=phys # The IPoIB kernel module is fussy, set device name to ib0 if not overridden
fi CONTAINER_IFNAME=${CONTAINER_IFNAME:-ib0}
else else IFTYPE=phys
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"
fi fi
IFTYPE=bridge else
BRTYPE=openvswitch # case "$IFNAME" in
;; # br*)
route*) IFTYPE=bridge
IFTYPE=route BRTYPE=linux
;; # ;;
dummy*) # ovs*)
IFTYPE=dummy # if ! $(which ovs-vsctl >/dev/null)
;; # then
*) die 1 "I do not know how to setup interface $IFNAME." ;; # echo "Need OVS installed on the system to create an ovs bridge"
esac # exit 1
fi # fi
# IFTYPE=bridge
# BRTYPE=openvswitch
# ;;
# *)
# echo "I do not know how to setup interface $IFNAME."
# exit 1
# ;;
# esac
fi
fi fi
# Set the default container interface name to eth1 if not already set # Set the default container interface name to eth1 if not already set
CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1} CONTAINER_IFNAME=${CONTAINER_IFNAME:-eth1}
[ "$WAIT" ] && { [ "$WAIT" ] && {
while true; do while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null
# This first method works even without `ip` or `ifconfig` installed, do sleep 1
# but doesn't work on older kernels (e.g. CentOS 6.X). See #128. done
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
exit 0 exit 0
} }
[ "$IFTYPE" = bridge ] && [ "$BRTYPE" = linux ] && [ "$VLAN" ] && { [ $IFTYPE = bridge ] && [ $BRTYPE = linux ] && [ "$VLAN" ] && {
die 1 "VLAN configuration currently unsupported for Linux bridge." echo "VLAN configuration currently unsupported for Linux bridge."
exit 1
} }
[ "$IFTYPE" = ipoib ] && [ "$MACADDR" ] && { [ $IFTYPE = ipoib ] && [ $MACADDR ] && {
die 1 "MACADDR configuration unsupported for IPoIB interfaces." echo "MACADDR configuration unsupported for IPoIB interfaces."
exit 1
} }
# Second step: find the guest (for now, we only support LXC containers) # Second step: find the guest (for now, we only support LXC containers)
while read _ mnt fstype options _; do while read dev mnt fstype options dump fsck
[ "$fstype" != "cgroup" ] && continue do
echo "$options" | grep -qw devices || continue [ "$fstype" != "cgroup" ] && continue
CGROUPMNT=$mnt echo $options | grep -qw devices || continue
CGROUPMNT=$mnt
done < /proc/mounts done < /proc/mounts
[ "$CGROUPMNT" ] || { [ "$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. # Try to find a cgroup matching exactly the provided name.
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l) N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
case "$N" in case "$N" in
0) 0)
# If we didn't find anything, try to lookup the container with Docker. # If we didn't find anything, try to lookup the container with Docker.
if installed docker; then if which docker >/dev/null
RETRIES=3 then
while [ "$RETRIES" -gt 0 ]; do RETRIES=3
DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' "$GUESTNAME") while [ $RETRIES -gt 0 ]; do
[ "$DOCKERPID" != 0 ] && break DOCKERPID=$(docker inspect --format='{{ .State.Pid }}' $GUESTNAME)
sleep 1 [ $DOCKERPID != 0 ] && break
RETRIES=$((RETRIES - 1)) sleep 1
done RETRIES=$((RETRIES - 1))
done
[ "$DOCKERPID" = 0 ] && { [ "$DOCKERPID" = 0 ] && {
die 1 "Docker inspect returned invalid PID 0" echo "Docker inspect returned invalid PID 0"
} exit 1
}
[ "$DOCKERPID" = "<no value>" ] && { [ "$DOCKERPID" = "<no value>" ] && {
die 1 "Container $GUESTNAME not found, and unknown to Docker." echo "Container $GUESTNAME not found, and unknown to Docker."
} exit 1
else }
die 1 "Container $GUESTNAME not found, and Docker not installed." else
fi echo "Container $GUESTNAME not found, and Docker not installed."
;; exit 1
1) true ;; fi
*) die 1 "Found more than one container matching $GUESTNAME." ;; ;;
1)
true
;;
*)
echo "Found more than one container matching $GUESTNAME."
exit 1
;;
esac esac
# only check IPADDR if we are not in a route mode if [ "$IPADDR" = "dhcp" ]
[ "$IFTYPE" != route ] && { then
case "$IPADDR" in # Check for first available dhcp client
# Let's check first if the user asked for DHCP allocation. DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient"
dhcp|dhcp:*) for CLIENT in $DHCP_CLIENT_LIST; do
# Use Docker-specific strategy to run the DHCP client which $CLIENT >/dev/null && {
# from the busybox image, in the network namespace of DHCP_CLIENT=$CLIENT
# the container. break
if ! [ "$DOCKERPID" ]; then }
warn "You asked for a Docker-specific DHCP method." done
warn "However, $GUESTNAME doesn't seem to be a Docker container." [ -z $DHCP_CLIENT ] && {
warn "Try to replace 'dhcp' with another option?" echo "You asked for DHCP; but no DHCP client could be found."
die 1 "Aborting." exit 1
fi }
DHCP_CLIENT=${IPADDR%%:*} else
;; # Check if a subnet mask was provided.
udhcpc|udhcpc:*|udhcpc-f|udhcpc-f:*|dhcpcd|dhcpcd:*|dhclient|dhclient:*|dhclient-f|dhclient-f:*) echo $IPADDR | grep -q / || {
DHCP_CLIENT=${IPADDR%%:*} echo "The IP address should include a netmask."
# did they ask for the client to remain? echo "Maybe you meant $IPADDR/24 ?"
DHCP_FOREGROUND= exit 1
[ "${DHCP_CLIENT: -2}" = '-f' ] && { }
DHCP_FOREGROUND=true # Check if a gateway address was provided.
} if echo $IPADDR | grep -q @
DHCP_CLIENT=${DHCP_CLIENT%-f} then
if ! installed "$DHCP_CLIENT"; then GATEWAY=$(echo $IPADDR | cut -d@ -f2)
die 1 "You asked for DHCP client $DHCP_CLIENT, but I can't find it." IPADDR=$(echo $IPADDR | cut -d@ -f1)
fi else
;; GATEWAY=
# Alright, no DHCP? Then let's see if we have a subnet *and* gateway. fi
*/*@*)
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
fi fi
if [ "$DOCKERPID" ]; then if [ $DOCKERPID ]; then
NSPID=$DOCKERPID NSPID=$DOCKERPID
else 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" ] || { [ "$NSPID" ] || {
# it is an alternative way to get the pid echo "Could not find a process inside container $GUESTNAME."
NSPID=$(lxc-info -n "$GUESTNAME" | grep PID | grep -Eo '[0-9]+') exit 1
[ "$NSPID" ] || {
die 1 "Could not find a process inside container $GUESTNAME."
}
} }
fi fi
# Check if an incompatible VLAN device already exists # Check if an incompatible VLAN device already exists
[ "$IFTYPE" = phys ] && [ "$VLAN" ] && [ -d "/sys/class/net/$IFNAME.VLAN" ] && { [ $IFTYPE = phys ] && [ "$VLAN" ] && [ -d /sys/class/net/$IFNAME.VLAN ] && {
ip -d link show "$IFNAME.$VLAN" | grep -q "vlan.*id $VLAN" || { [ -z "$(ip -d link show $IFNAME.$VLAN | grep "vlan.*id $VLAN")" ] && {
die 1 "$IFNAME.VLAN already exists but is not a VLAN device for tag $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 [ ! -d /var/run/netns ] && mkdir -p /var/run/netns
rm -f "/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" ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID
# Check if we need to create a bridge. # Check if we need to create a bridge.
[ "$IFTYPE" = bridge ] && [ ! -d "/sys/class/net/$IFNAME" ] && { [ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && {
[ "$BRTYPE" = linux ] && { [ $BRTYPE = linux ] && {
(ip link add dev "$IFNAME" type bridge > /dev/null 2>&1) || (brctl addbr "$IFNAME") (ip link add dev $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME)
ip link set "$IFNAME" up ip link set $IFNAME up
} }
[ "$BRTYPE" = openvswitch ] && { [ $BRTYPE = openvswitch ] && {
ovs-vsctl add-br "$IFNAME" 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 # If it's a bridge, we need to create a veth pair
[ "$IFTYPE" = bridge ] && { [ $IFTYPE = bridge ] && {
if [ -z "$LOCAL_IFNAME" ]; then
LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}" LOCAL_IFNAME="v${CONTAINER_IFNAME}pl${NSPID}"
fi GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}"
GUEST_IFNAME="v${CONTAINER_IFNAME}pg${NSPID}" ip link add name $LOCAL_IFNAME mtu $MTU type veth peer name $GUEST_IFNAME mtu $MTU
# Does the link already exist? case "$BRTYPE" in
if ip link show "$LOCAL_IFNAME" >/dev/null 2>&1; then linux)
# link exists, is it in use? (ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME)
if ip link show "$LOCAL_IFNAME" up | grep -q "UP"; then ;;
echo "Link $LOCAL_IFNAME exists and is up" openvswitch)
exit 1 ovs-vsctl add-port $IFNAME $LOCAL_IFNAME ${VLAN:+"tag=$VLAN"}
fi ;;
# delete the link so we can re-add it afterwards esac
ip link del "$LOCAL_IFNAME" ip link set $LOCAL_IFNAME up
fi }
ip link add name "$LOCAL_IFNAME" mtu "$MTU" type veth peer name "$GUEST_IFNAME" mtu "$MTU"
case "$BRTYPE" in # Note: if no container interface name was specified, pipework will default to ib0
linux) # Note: no macvlan subinterface or ethernet bridge can be created against an
(ip link set "$LOCAL_IFNAME" master "$IFNAME" > /dev/null 2>&1) || (brctl addif "$IFNAME" "$LOCAL_IFNAME") # 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
openvswitch) # to assign them.
if ! ovs-vsctl list-ports "$IFNAME" | grep -q "^${LOCAL_IFNAME}$"; then [ $IFTYPE = ipoib ] && {
ovs-vsctl add-port "$IFNAME" "$LOCAL_IFNAME" ${VLAN:+tag="$VLAN"} GUEST_IFNAME=$CONTAINER_IFNAME
fi
;;
esac
ip link set "$LOCAL_IFNAME" up
} }
# If it's a physical interface, create a macvlan subinterface # If it's a physical interface, create a macvlan subinterface
[ "$IFTYPE" = phys ] && { [ $IFTYPE = phys ] && {
[ "$VLAN" ] && { [ "$VLAN" ] && {
[ ! -d "/sys/class/net/${IFNAME}.${VLAN}" ] && { [ ! -d /sys/class/net/$IFNAME.$VLAN ] && {
ip link add link "$IFNAME" name "$IFNAME.$VLAN" mtu "$MTU" type vlan id "$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 GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
IFNAME=$IFNAME.$VLAN 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 ip link set $GUEST_IFNAME netns $NSPID
# equivalent of a macvlan device) 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
# Note: no macvlan subinterface nor Ethernet bridge can be created on top of an if [ "$IPADDR" = "dhcp" ]
# IPoIB interface. InfiniBand is not Ethernet. IPoIB is an IP layer on top of then
# InfiniBand, without an intermediate Ethernet layer. [ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME -x hostname:$GUESTNAME
[ "$IFTYPE" = ipoib ] && { if [ $DHCP_CLIENT = "dhclient" ]
GUEST_IFNAME="${IFNAME}.${NSPID}" then
# kill dhclient after get ip address to prevent device be used after container close
# If a partition key is provided, use it ip netns exec $NSPID $DHCP_CLIENT -pf "/var/run/dhclient.$NSPID.pid" $CONTAINER_IFNAME
[ "$PKEY" ] && { kill "$(cat "/var/run/dhclient.$NSPID.pid")"
GUEST_IFNAME="${IFNAME}.${PKEY}.${NSPID}" rm "/var/run/dhclient.$NSPID.pid"
PKEY="pkey 0x$PKEY" fi
} [ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME -h $GUESTNAME
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 else
# Otherwise, run normally. ip netns exec $NSPID ip addr add $IPADDR dev $CONTAINER_IFNAME
ip link set "$GUEST_IFNAME" netns "$NSPID" [ "$GATEWAY" ] && {
ip netns exec "$NSPID" ip link set "$GUEST_IFNAME" name "$CONTAINER_IFNAME" ip netns exec $NSPID ip route delete default >/dev/null 2>&1 && true
[ "$MACADDR" ] && ip netns exec "$NSPID" ip link set dev "$CONTAINER_IFNAME" address "$MACADDR" }
ip netns exec $NSPID ip link set $CONTAINER_IFNAME up
# When using any of the DHCP methods, we start a DHCP client in the [ "$GATEWAY" ] && {
# network namespace of the container. With the 'dhcp' method, the ip netns exec $NSPID ip route get $GATEWAY >/dev/null 2>&1 || \
# client used is taken from the Docker busybox image (therefore ip netns exec $NSPID ip route add $GATEWAY/32 dev $CONTAINER_IFNAME
# requiring no specific client installed on the host). Other methods ip netns exec $NSPID ip route replace default via $GATEWAY
# 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
fi 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.cache.auto_detect = true
config.vm.provider :lxc do |lxc|
# lxc.sudo_wrapper = '/usr/bin/lxc-vagrant-wrapper'
end
config.vm.provision :shell, config.vm.provision :shell,
inline: 'mkdir -p /vagrant/tmp && echo -n "Provisioned" > /vagrant/tmp/provisioning' 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_chain(:config, :vm, :networks).and_return(networks)
machine.stub(provider: provider, data_dir: data_dir) machine.stub(provider: provider, data_dir: data_dir)
subject.stub(redir_version: 3)
subject.stub(exec: true) subject.stub(exec: true)
subject.stub(spawn: pid) subject.stub(spawn: pid)
end end
@ -35,25 +34,25 @@ describe Vagrant::LXC::Action::ForwardPorts do
subject.stub(system: true) subject.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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 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) forward_conf.delete(:host_ip)
subject.stub(system: true) subject.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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 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] = ' ' forward_conf[:host_ip] = ' '
subject.stub(system: true) subject.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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 end
@ -71,15 +70,6 @@ describe Vagrant::LXC::Action::ForwardPorts do
expect(subject).not_to have_received(:spawn) expect(subject).not_to have_received(:spawn)
end 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 it 'raises RedirNotInstalled error if `redir` is not installed' do
subject.stub(system: false) subject.stub(system: false)
expect { subject.call(env) }.to raise_error(Vagrant::LXC::Errors::RedirNotInstalled) 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.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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 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) forward_conf.delete(:host_ip)
subject.stub(system: true) subject.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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
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] = ' ' forward_conf[:host_ip] = ' '
subject.stub(system: true) subject.stub(system: true)
subject.call(env) subject.call(env)
expect(subject).to have_received(:spawn).with( 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
end end

View file

@ -30,7 +30,16 @@ describe Vagrant::LXC::Driver::CLI do
describe 'version' do describe 'version' do
before 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 end
describe 'lxc version after 1.x.x' do describe 'lxc version after 1.x.x' do
@ -44,11 +53,24 @@ describe Vagrant::LXC::Driver::CLI do
describe 'config' do describe 'config' do
before 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(: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) allow(subject).to receive(:run).with(:create, '--version').and_return(lxc_version_out)
end 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_config_out) { "/var/lib/lxc\n" }
let(:lxc_version_out) { "1.0.0\n" } let(:lxc_version_out) { "1.0.0\n" }
@ -134,11 +156,36 @@ describe Vagrant::LXC::Driver::CLI do
before do before do
allow(subject).to receive(:run) allow(subject).to receive(:run)
subject.stop
end end
it 'issues a lxc-stop with provided container name' do context 'lxc-attach is supported' do
expect(subject).to have_received(:run).with(:stop, '--name', name) 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
end end
@ -185,6 +232,13 @@ describe Vagrant::LXC::Driver::CLI do
subject.attach *(command + [{namespaces: ['network', 'mount']}]) subject.attach *(command + [{namespaces: ['network', 'mount']}])
expect(subject).to have_received(:run).with(:attach, '--name', name, '--namespaces', 'NETWORK|MOUNT', '--', *command) expect(subject).to have_received(:run).with(:attach, '--name', name, '--namespaces', 'NETWORK|MOUNT', '--', *command)
end 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 end
describe 'transition block' do describe 'transition block' do
@ -206,4 +260,33 @@ describe Vagrant::LXC::Driver::CLI do
skip 'waits for the expected container state' skip 'waits for the expected container state'
end 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 end

View file

@ -54,7 +54,7 @@ describe Vagrant::LXC::Driver do
it 'creates container with the right arguments' do it 'creates container with the right arguments' do
expect(cli).to have_received(:create).with( expect(cli).to have_received(:create).with(
template_path, template_name,
backingstore, backingstore,
backingstore_opts, backingstore_opts,
config_file, config_file,
@ -75,10 +75,21 @@ describe Vagrant::LXC::Driver do
end end
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 describe 'start' do
let(:customizations) { [['a', '1'], ['b', '2']] } let(:customizations) { [['a', '1'], ['b', '2']] }
let(:internal_customization) { ['internal', 'customization'] } 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) } let(:sudo) { double(Vagrant::LXC::SudoWrapper) }
subject { described_class.new('name', sudo, cli) } subject { described_class.new('name', sudo, cli) }
@ -86,9 +97,8 @@ describe Vagrant::LXC::Driver do
before do before do
sudo.should_receive(:run).with('cat', '/var/lib/lxc/name/config').exactly(2).times. sudo.should_receive(:run).with('cat', '/var/lib/lxc/name/config').exactly(2).times.
and_return('# CONFIGURATION') 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') 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.customizations << internal_customization
subject.start(customizations) subject.start(customizations)
@ -142,11 +152,21 @@ describe Vagrant::LXC::Driver do
end end
describe 'containers_path' do 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) } 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 describe 'lxc version after 1.x.x' do
let(:cli_support_config_command_value) { true }
let(:cli_config_value) { '/etc/lxc' } let(:cli_config_value) { '/etc/lxc' }
it 'delegates to cli' do it 'delegates to cli' do
@ -172,10 +192,14 @@ describe Vagrant::LXC::Driver do
subject.share_folders(folders) subject.share_folders(folders)
end 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 it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [ expect(subject.customizations).to include [
'mount.entry', '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
@ -189,7 +213,7 @@ describe Vagrant::LXC::Driver do
it 'supports directories with spaces' do it 'supports directories with spaces' do
expect(subject.customizations).to include [ expect(subject.customizations).to include [
'mount.entry', '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
end end
@ -200,9 +224,9 @@ describe Vagrant::LXC::Driver do
# Blah blah comment # Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0 lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4 lxc.tty = 4
lxc.pty.max = 1024 lxc.pts = 1024
lxc.rootfs.path = #{rootfs_path} lxc.rootfs = #{rootfs_path}
# VAGRANT-BEGIN # VAGRANT-BEGIN
lxc.network.type=veth lxc.network.type=veth
lxc.network.name=eth1 lxc.network.name=eth1
@ -219,7 +243,7 @@ describe Vagrant::LXC::Driver do
it 'adds a mount.entry to its local customizations' do it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [ expect(subject.customizations).to include [
'mount.entry', '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
end end
@ -230,9 +254,9 @@ describe Vagrant::LXC::Driver do
# Blah blah comment # Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0 lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0 lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4 lxc.tty = 4
lxc.pty.max = 1024 lxc.pts = 1024
lxc.rootfs.path = overlayfs:/path/to/master/directory:#{rootfs_path} lxc.rootfs = overlayfs:/path/to/master/directory:#{rootfs_path}
# VAGRANT-BEGIN # VAGRANT-BEGIN
lxc.network.type=veth lxc.network.type=veth
lxc.network.name=eth1 lxc.network.name=eth1
@ -249,7 +273,7 @@ describe Vagrant::LXC::Driver do
it 'adds a mount.entry to its local customizations' do it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [ expect(subject.customizations).to include [
'mount.entry', '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
end end

View file

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