Compare commits

..

No commits in common. "master" and "v0.0.1" have entirely different histories.

95 changed files with 2522 additions and 4365 deletions

6
.gitignore vendored
View file

@ -19,13 +19,11 @@ doc/
/tags
/gems.tags
/Gemfile.lock
.vagrant
/cache
/boxes/**/*.tar.gz
/boxes/**/partial/
/boxes/**/rootfs/
/boxes/temp/
/boxes/quantal64/rootfs-amd64/
/boxes/output/
/development/Vagrantfile

3
.gitmodules vendored Normal file
View file

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

View file

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

2
.vimrc
View file

@ -1 +1 @@
set wildignore+=*/boxes/*/rootfs/*,*/boxes/*/partial/*
set wildignore+=*/vagrant-1.1/*

View file

@ -1,47 +0,0 @@
# vagrant-lxc base boxes
Although the official documentation says it is only supported for VirtualBox
environments, you can use the [`vagrant package`](http://docs.vagrantup.com/v2/cli/package.html)
command to export a `.box` file from an existing vagrant-lxc container.
There is also a set of [bash scripts](https://github.com/fgrehm/vagrant-lxc-base-boxes)
that you can use to build base boxes as needed. By default it won't include any
provisioning tool and you can pick the ones you want by providing some environment
variables. Please refer to the [base boxes repository](https://github.com/fgrehm/vagrant-lxc-base-boxes)
for more information.
## "Anatomy" of a box
If you need to go deeper and build your scripts from scratch or if you are interested
on knowing what makes a base box for vagrant-lxc, here's what's needed:
### Expected `.box` contents
| FILE | REQUIRED? | DESCRIPTION |
| --- | --- | --- |
| `metadata.json` | Yes | Required by Vagrant |
| `rootfs.tar.gz` | Yes | Compressed container rootfs tarball (need to remeber to pass in `--numeric-owner` when creating it) |
| `lxc-template` | No, a ["generic script"](scripts/lxc-template) is provided by the plugin if it doesn't exist on the base box | Script responsible for creating and setting up the container (used with `lxc-create`). |
| `lxc-config` | No | Box specific configuration to be _appended_ to the system's generated container config file |
| `lxc.conf` | No | File passed in to `lxc-create -f` |
### metadata.json
```json
{
"provider": "lxc",
"version": "1.0.0",
"built-on": "Sat Sep 21 21:10:00 UTC 2013",
"template-opts": {
"--arch": "amd64",
"--release": "quantal"
}
}
```
| KEY | REQUIRED? | DESCRIPTION |
| --- | --- | --- |
| `provider` | Yes | Required by Vagrant |
| `version` | Yes | Tracks backward incompatibilities |
| `built-on` | No | Date / time when the box was packaged for the first time |
| `template-opts` | No | Extra options to be passed to the `lxc-template` script |

View file

@ -1,523 +0,0 @@
## [1.4.2](https://github.com/fgrehm/vagrant-lxc/compare/v1.4.1...v1.4.2) (Jul 17, 2018)
FIXES:
- Fix problems with `redir` 3.x command line. [[GH-467]]
## [1.4.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.4.0...v1.4.1) (Apr 30, 2018)
FEATURES:
- Add support for LXC v3.0
- Add support for `redir` 3.x command line. [[GH-460]]
[GH-460]: https://github.com/fgrehm/vagrant-lxc/issues/460
## [1.4.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.3.1...v1.4.0) (Mar 04, 2018)
FEATURES:
- Add support for unprivileged containers. [[GH-312]]
[GH-312]: https://github.com/fgrehm/vagrant-lxc/issues/312
## [1.3.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.3.0...v1.3.1) (Fev 06, 2018)
FIXES:
- Fix problems with `tmpfs` fiddling in v1.3.0. [[GH-455]]
[GH-455]: https://github.com/fgrehm/vagrant-lxc/pull/455
## [1.3.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.4...v1.3.0) (Jan 20, 2018)
FEATURES:
- lxc-template: make runnable by unprivileged users [[GH-447]]
- Use `lxc-info` instead of `lxc-attach` to retrieve container IP
- Add support for LXC v2.1+ [[GH-445]]
- Remove 2Gb limitation on `/tmp`. [[GH-406]]
OTHERS:
- Bump Vagrant requirements to v1.8+
- Bump LXC requirements to v1.0+
[GH-447]: https://github.com/fgrehm/vagrant-lxc/pull/447
[GH-445]: https://github.com/fgrehm/vagrant-lxc/pull/445
[GH-406]: https://github.com/fgrehm/vagrant-lxc/pull/406
## [1.2.4](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.3...v1.2.4) (Dec 20, 2017)
BUGFIX:
- Support alternative `lxcpath` [[GH-413]]
- Update `pipework` regexp in sudo wrapper for Vagrant 1.9+ [[GH-438]]
- Work around restrictive `umask` values [[GH-435]]
- Make `--config` in `lxc-template` optional [[GH-421]]
- Fix sudo wrapper binpath construction logic [[GH-410]]
- Fix bug causing CTRL-C on `vagrant up` to destroy the VM [[GH-449]]
[GH-413]: https://github.com/fgrehm/vagrant-lxc/pull/413
[GH-438]: https://github.com/fgrehm/vagrant-lxc/pull/438
[GH-435]: https://github.com/fgrehm/vagrant-lxc/pull/435
[GH-421]: https://github.com/fgrehm/vagrant-lxc/pull/421
[GH-410]: https://github.com/fgrehm/vagrant-lxc/pull/410
[GH-449]: https://github.com/fgrehm/vagrant-lxc/pull/449
## [1.2.3](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.2...v1.2.3) (Dec 20, 2016)
- Fix bug in Gemfile.lock
## [1.2.2](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.1...v1.2.2) (Dec 20, 2016)
BUGFIX:
- Make the timeout for fetching container IP's configurable [[GH-426]]
- Load locale file only once [[GH-423]]
- Preserve xattrs in container filesystems [[GH-411]]
- Forward port latest pipework script [[GH-408]]
- Fix handling of non-fatal lxc-stop return code [[GH-405]]
[GH-426]: https://github.com/fgrehm/vagrant-lxc/pull/426
[GH-423]: https://github.com/fgrehm/vagrant-lxc/pull/423
[GH-411]: https://github.com/fgrehm/vagrant-lxc/pull/411
[GH-408]: https://github.com/fgrehm/vagrant-lxc/pull/408
[GH-405]: https://github.com/fgrehm/vagrant-lxc/pull/405
## [1.2.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.2.0...v1.2.1) (Sep 24, 2015)
BUGFIX:
- Fix sudo Wrapper [[GH-393]]
[GH-393]: https://github.com/fgrehm/vagrant-lxc/pull/393
## [1.2.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.1.0...v1.2.0) (Sep 15, 2015)
FEATURES:
- Support private networking using DHCP [[GH-352]]
[GH-352]: https://github.com/fgrehm/vagrant-lxc/pull/352
IMPROVEMENTS:
- Move mountpoint creation to lxc template for lvm rootfs support [[GH-361]] / [[GH-359]]
- Mount selinux sys dir read-only [[GH-357]] / [[GH-301]]
- Use correct ruby interpreter when generating sudoers file [[GH-355]]
- Fix shebangs to be more portable [[GH-376]]
- Fix removal of lxcbr0/virbr0 when using private networking [[GH-383]]
- Improve /tmp handling by using tmpfs [[GH-362]]
[GH-301]: https://github.com/fgrehm/vagrant-lxc/issues/301
[GH-355]: https://github.com/fgrehm/vagrant-lxc/pull/355
[GH-357]: https://github.com/fgrehm/vagrant-lxc/pull/357
[GH-359]: https://github.com/fgrehm/vagrant-lxc/issues/359
[GH-361]: https://github.com/fgrehm/vagrant-lxc/pull/361
[GH-376]: https://github.com/fgrehm/vagrant-lxc/pull/376
[GH-383]: https://github.com/fgrehm/vagrant-lxc/pull/383
[GH-362]: https://github.com/fgrehm/vagrant-lxc/pull/362
## [1.1.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.1...v1.1.0) (Jan 14, 2015)
BACKWARDS INCOMPATIBILITIES:
- Support for Vagrant versions prior to 1.5 have been removed. The plugin now targets
Vagrant 1.7+ but it _might_ work on 1.5+.
FEATURES:
- New experimental support for private networking [[GH-298]] / [[GH-120]].
- Support for formatted overlayfs path [[GH-329]]
[GH-298]: https://github.com/fgrehm/vagrant-lxc/pull/298
[GH-120]: https://github.com/fgrehm/vagrant-lxc/issues/120
[GH-329]: https://github.com/fgrehm/vagrant-lxc/pull/329
IMPROVEMENTS:
- The provider will now have a higher priority over the VirtualBox provider
in case VirtualBox is installed alongside lxc dependecies.
- Show an user friendly message when trying to use the plugin on non-Linux
environments.
BUG FIXES:
- Allow backingstore options to be used along with the sudo wrapper script [[GH-310]]
- Trim automatically generated container names to 64 chars [[GH-337]]
[GH-337]: https://github.com/fgrehm/vagrant-lxc/issues/337
[GH-310]: https://github.com/fgrehm/vagrant-lxc/issues/310
## [1.0.1](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.0...v1.0.1) (Oct 15, 2014)
IMPROVEMENTS:
- Avoid lock race condition when fetching container's IP [[GH-318]] and SSH execution [[GH-321]]
- Support for custom containers storage path by reading `lxc.lxcpath` [[GH-317]]
[GH-317]: https://github.com/fgrehm/vagrant-lxc/pull/317
[GH-318]: https://github.com/fgrehm/vagrant-lxc/pull/318
[GH-321]: https://github.com/fgrehm/vagrant-lxc/issues/321
## [1.0.0](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.0.alpha.3...v1.0.0) (Sep 23, 2014)
DEPRECATIONS:
- Support to **all Vagrant versions prior to 1.5 are deprecated**, there is a
[small layer](lib/vagrant-backports) that ensures compatibility with versions
starting with 1.1.5 that will be removed on a future release.
- Official base boxes that were made available from http://bit.ly are no longer
supported and were removed from @fgrehm's Dropbox, please upgrade your Vagrant
and vagrant-lxc installation and use a base box from [VagrantCloud](https://vagrantcloud.com/search?provider=lxc)
BACKWARDS INCOMPATIBILITIES:
- Remove plugin version from config file name generated by the `vagrant lxc sudoers`
command. Manual removal of `/usr/local/bin/vagrant-lxc-wrapper-*` / `/etc/sudoers.d/vagrant-lxc-*`
files are required.
IMPROVEMENTS:
- `vagrant-mounted` upstart event is now emited on containers that support it [[GH-302]]
- Add support for specifying the `--strip-parameters` used by the [default template](scripts/lxc-template)
when extracting rootfs tarballs [[GH-311]]
[GH-302]: https://github.com/fgrehm/vagrant-lxc/issues/302
BUG FIXES:
- Check for outdated base boxes when starting containers [[GH-314]]
[GH-311]: https://github.com/fgrehm/vagrant-lxc/pull/311
[GH-314]: https://github.com/fgrehm/vagrant-lxc/pull/314
## [1.0.0.alpha.3](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.0.alpha.2...v1.0.0.alpha.3) (Aug 9, 2014)
IMPROVEMENTS:
- Remove `lxc-shutdown` usage in favor of Vagrant's built in graceful halt
- Add fallback mechanism for platforms without `lxc-attach` support [[GH-294]]
[GH-294]: https://github.com/fgrehm/vagrant-lxc/pull/294
BUG FIXES:
- Figure out the real executable paths for whitelisted commands on the sudo
wrapper script instead of hardcoding Ubuntu paths [[GH-304]] / [[GH-305]]
- Attach to containers using the `MOUNT` namespace when attempting to fetch
container's IP [[GH-300]]
- Escape space characters for synced folders [[GH-291]]
- Use Vagrant's ruby on the sudoers file so that it works on systems that don't
have a global ruby installation [[GH-289]]
[GH-304]: https://github.com/fgrehm/vagrant-lxc/issues/304
[GH-305]: https://github.com/fgrehm/vagrant-lxc/issues/305
[GH-300]: https://github.com/fgrehm/vagrant-lxc/issues/300
[GH-291]: https://github.com/fgrehm/vagrant-lxc/issues/291
[GH-289]: https://github.com/fgrehm/vagrant-lxc/issues/289
## [1.0.0.alpha.2](https://github.com/fgrehm/vagrant-lxc/compare/v1.0.0.alpha.1...v1.0.0.alpha.2) (May 13, 2014)
BACKWARDS INCOMPATIBILITIES:
- The `sudo_wrapper` provider configuration was removed in favor of using the
secure wrapper generated by `vagrant lxc sudoers` [[GH-272]]
- Support for specifying backingstore parameters from `Vagrantfile`s for `lxc-create`
was added and it defaults to the `best` option. On older lxc versions that does not
support that value, it needs to be set to `none`.
FEATURES:
- Add support for specifying backingstore parameters from `Vagrantfile`s [[GH-277]]
IMPROVEMENTS:
- Make `dnsmasq` leases MAC address regex check case insensitive [[GH-283]]
- Use relative paths for `lxc.mount.entry` to avoid issues with `lxc-clone` [[GH-258]].
- Sort synced folders when mounting [[GH-271]]
- Privileged ports can now be forwarded with `sudo` [[GH-259]]
- The `vagrant lxc sudoers` generated sudoers configuration and wrapper script
are safer and properly whitelists the commands required by vagrant-lxc to run.
[[GH-272]] / [[GH-269]]
BUG FIXES:
- Fix `lxc-create` issues with pre 1.0.0 versions [[GH-282]]
[GH-283]: https://github.com/fgrehm/vagrant-lxc/pull/283
[GH-282]: https://github.com/fgrehm/vagrant-lxc/pull/282
[GH-269]: https://github.com/fgrehm/vagrant-lxc/issues/269
[GH-272]: https://github.com/fgrehm/vagrant-lxc/pull/272
[GH-259]: https://github.com/fgrehm/vagrant-lxc/pull/259
[GH-271]: https://github.com/fgrehm/vagrant-lxc/pull/271
[GH-277]: https://github.com/fgrehm/vagrant-lxc/pull/277
[GH-258]: https://github.com/fgrehm/vagrant-lxc/issues/258
## [1.0.0.alpha.1](https://github.com/fgrehm/vagrant-lxc/compare/v0.8.0...v1.0.0.alpha.1) (Apr 06, 2014)
DEPRECATIONS:
- Support to **all Vagrant versions prior to 1.5 are now deprecated**, there is a
[small layer](lib/vagrant-backports) that ensures compatibility with versions
starting with 1.1.5 but there is no guarantee that it will stick for too long.
- Boxes released prior to this version are now deprecated and won't be available
after the final 1.0.0 release.
- `--auth-key` argument is no longer provided to `lxc-template`. This will cause
all official base boxes prior to 09/28/2013 to break.
FEATURES:
- New `vagrant lxc sudoers` command for creating a policy for users in order to
avoid `sudo` passwords [[GH-237]] / [[GH-257]]
- Support for NFS and rsync synced folders.
- Support for synced folder mount options allowing for using read only synced
folders [[GH-193]]
[GH-237]: https://github.com/fgrehm/vagrant-lxc/issues/237
[GH-257]: https://github.com/fgrehm/vagrant-lxc/pull/257
[GH-193]: https://github.com/fgrehm/vagrant-lxc/issues/193
IMPROVEMENTS:
- `lxc-template` is now optional for base boxes and are bundled with the plugin,
allowing us to roll out updates without the need to rebuild boxes [[GH-254]]
- Set container's `utsname` to `config.vm.hostname` by default [[GH-253]]
- Added libvirt dnsmasq leases file to the lookup paths [[GH-251]]
- Improved compatibility with Vagrant 1.4 / 1.5 including the ability
to use `rsync` and `nfs` shared folders to work around synced folders
permission problems. More information can be found on the following
issues: [[GH-151]] [[GH-191]] [[GH-241]] [[GH-242]]
- Warn in case `:group` or `:owner` are specified for synced folders [[GH-196]]
- Acceptance specs are now powered by `vagrant-spec` [[GH-213]]
- Base boxes creation scripts were moved out to https://github.com/fgrehm/vagrant-lxc-base-boxes.
[GH-254]: https://github.com/fgrehm/vagrant-lxc/issues/254
[GH-196]: https://github.com/fgrehm/vagrant-lxc/issues/196
[GH-251]: https://github.com/fgrehm/vagrant-lxc/pull/251
[GH-253]: https://github.com/fgrehm/vagrant-lxc/pull/253
[GH-151]: https://github.com/fgrehm/vagrant-lxc/issues/151
[GH-213]: https://github.com/fgrehm/vagrant-lxc/issues/213
[GH-191]: https://github.com/fgrehm/vagrant-lxc/issues/191
[GH-241]: https://github.com/fgrehm/vagrant-lxc/issues/241
[GH-242]: https://github.com/fgrehm/vagrant-lxc/issues/242
## [0.8.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.7.0...v0.8.0) (Feb 26, 2014)
FEATURES:
- Support for naming containers from Vagrantfiles [#132](https://github.com/fgrehm/vagrant-lxc/issues/132)
IMPROVEMENTS:
- Use a safer random name for containers [#152](https://github.com/fgrehm/vagrant-lxc/issues/152)
- Improve Ubuntu 13.10 compatibility [#190](https://github.com/fgrehm/vagrant-lxc/pull/190) / [#197](https://github.com/fgrehm/vagrant-lxc/pull/197)
- Improved mac address detection from lxc configs [#226](https://github.com/fgrehm/vagrant-lxc/pull/226)
BUG FIXES:
- Properly detect if lxc is installed on hosts that do not have `lxc-version` on their paths [#186](https://github.com/fgrehm/vagrant-lxc/issues/186)
## [0.7.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.6.4...v0.7.0) (Nov 8, 2013)
IMPROVEMENTS:
- Support for `vagrant up` in parallel [#152](https://github.com/fgrehm/vagrant-lxc/issues/152)
- Warn users about unsupported private / public networking configs [#154](https://github.com/fgrehm/vagrant-lxc/issues/154)
- Respect Vagrantfile options to disable forwarded port [#149](https://github.com/fgrehm/vagrant-lxc/issues/149)
BUG FIXES:
- Nicely handle blank strings provided to `:host_ip` when specifying forwarded ports [#170](https://github.com/fgrehm/vagrant-lxc/issues/170)
- Fix "Permission denied" when starting/destroying containers after lxc
security update in Ubuntu [#180](https://github.com/fgrehm/vagrant-lxc/issues/180)
- Fix `vagrant package` [#172](https://github.com/fgrehm/vagrant-lxc/issues/172)
## [0.6.4](https://github.com/fgrehm/vagrant-lxc/compare/v0.6.3...v0.6.4) (Oct 27, 2013)
FEATURES:
- New script for building OpenMandriva base boxes [#167](https://github.com/fgrehm/vagrant-lxc/issues/167)
IMPROVEMENTS:
- Make `lxc-template` compatible with Ubuntu 13.10 [#150](https://github.com/fgrehm/vagrant-lxc/issues/150)
BUG FIXES:
- Fix force halt for hosts that do not have `lxc-shutdown` around (like Ubuntu 13.10) [#150](https://github.com/fgrehm/vagrant-lxc/issues/150)
## [0.6.3](https://github.com/fgrehm/vagrant-lxc/compare/v0.6.2...v0.6.3) (Oct 12, 2013)
IMPROVEMENTS:
- Respect Vagrantfile option to disable synced folders [#147](https://github.com/fgrehm/vagrant-lxc/issues/147)
BUG FIXES:
- Fix error raised when fetching container's IP with the sudo wrapper disabled [#157](https://github.com/fgrehm/vagrant-lxc/issues/157)
## [0.6.2](https://github.com/fgrehm/vagrant-lxc/compare/v0.6.1...v0.6.2) (Oct 03, 2013)
IMPROVEMENTS:
- Cache the result of `lxc-attach --namespaces` parameter support checking to
avoid excessive logging.
BUG FIXES:
- Fix detection of `lxc-attach --namespaces` parameter support checking.
## [0.6.1](https://github.com/fgrehm/vagrant-lxc/compare/v0.6.0...v0.6.1) (Oct 03, 2013)
IMPROVEMENTS:
- Fall back to `dnsmasq` leases file if not able to fetch IP with `lxc-attach` [#118](https://github.com/fgrehm/vagrant-lxc/issues/118)
- Make sure lxc templates are executable prior to `lxc-create` [#128](https://github.com/fgrehm/vagrant-lxc/issues/128)
- New base boxes with support for lxc 1.0+
BUG FIXES:
- Fix various issues related to detecting whether the container is running
and is "SSHable" [#142](https://github.com/fgrehm/vagrant-lxc/issues/142)
- Nicely handle missing templates path [#139](https://github.com/fgrehm/vagrant-lxc/issues/139)
## [0.6.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.5.0...v0.6.0) (Sep 12, 2013)
IMPROVEMENTS:
- Compatibility with Vagrant 1.3+ [#136](https://github.com/fgrehm/vagrant-lxc/pull/136)
- Set plugin name to `vagrant-lxc` so that it is easier to check if the plugin is
installed with the newly added `Vagrant.has_plugin?`
BUG FIXES:
- Fix box package ownership on `vagrant package` [#140](https://github.com/fgrehm/vagrant-lxc/pull/140)
- Fix error while compressing container's rootfs under Debian hosts [#131](https://github.com/fgrehm/vagrant-lxc/issues/131) /
[#133](https://github.com/fgrehm/vagrant-lxc/issues/133)
## [0.5.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.4.0...v0.5.0) (Aug 1, 2013)
BACKWARDS INCOMPATIBILITIES:
- To align with Vagrant's core behaviour, forwarded ports are no longer attached
to 127.0.0.1 and `redir`'s `--laddr` parameter is skipped in case the `:host_ip`
config is not provided, that means `redir` will listen on connections coming
from any of the host's IPs.
FEATURES:
- Add support for salt-minion and add latest dev release for ubuntu codenamed saucy [#116](https://github.com/fgrehm/vagrant-lxc/pull/116)
- Add support for using a sudo wrapper script [#90](https://github.com/fgrehm/vagrant-lxc/issues/90)
- `redir` will log to `/var/log/syslog` if `REDIR_LOG` env var is provided
IMPROVEMENTS:
- Error out if dependencies are not installed [#11](https://github.com/fgrehm/vagrant-lxc/issues/11) / [#112](https://github.com/fgrehm/vagrant-lxc/issues/112)
- Support for specifying host interface/ip for binding `redir` [#76](https://github.com/fgrehm/vagrant-lxc/issues/76)
- Add Vagrantfile VM name to the container name [#115](https://github.com/fgrehm/vagrant-lxc/issues/115)
- Properly handle forwarded port collisions [#5](https://github.com/fgrehm/vagrant-lxc/issues/5)
- Container's customizations are now written to the config file (usually
kept under `/var/lib/lxc/CONTAINER/config`) instead of passed in as a `-s`
parameter to `lxc-start`
## [0.4.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.3.4...v0.4.0) (Jul 18, 2013)
FEATURES:
- New box format [#89](https://github.com/fgrehm/vagrant-lxc/issues/89)
BUG FIXES:
- Add translation for stopped status [#97](https://github.com/fgrehm/vagrant-lxc/issues/97)
- Enable retries when fetching container state [#74](https://github.com/fgrehm/vagrant-lxc/issues/74)
- Fix error when setting Debian boxes hostname from Vagrantfile [#91](https://github.com/fgrehm/vagrant-lxc/issues/91)
- BTRFS-friendly base boxes [#81](https://github.com/fgrehm/vagrant-lxc/issues/81)
- Extended templates path lookup [#77](https://github.com/fgrehm/vagrant-lxc/issues/77) (tks to @aries1980)
- Fix default group for packaged boxes tarballs on the rake task [#82](https://github.com/fgrehm/vagrant-lxc/issues/82) (tks to @cduez)
## [0.3.4](https://github.com/fgrehm/vagrant-lxc/compare/v0.3.3...v0.3.4) (May 08, 2013)
FEATURES:
- Support for building Debian boxes (tks to @Val)
- Support for installing babushka on base boxes (tks to @Val)
IMPROVEMENTS:
- Replace `lxc-wait` usage with a "[retry mechanism](https://github.com/fgrehm/vagrant-lxc/commit/3cca16824879731315dac32bc2df1c643f30d461#L2R88)" [#22](https://github.com/fgrehm/vagrant-lxc/issues/22)
- Remove `/tmp` files after the machine has been successfully shut down [#68](https://github.com/fgrehm/vagrant-lxc/issues/68)
- Clean up base boxes files after they've been configured, resulting in smaller packages
- Bump development dependency to Vagrant 1.2+ series
BUG FIXES:
- Issue a `lxc-stop` in case the container cannot shutdown gracefully [#72](https://github.com/fgrehm/vagrant-lxc/issues/72)
## [0.3.3](https://github.com/fgrehm/vagrant-lxc/compare/v0.3.2...v0.3.3) (April 23, 2013)
BUG FIXES:
- Properly kill `redir` child processes [#59](https://github.com/fgrehm/vagrant-lxc/issues/59)
- Use `uname -m` on base Ubuntu lxc-template [#53](https://github.com/fgrehm/vagrant-lxc/issues/53)
IMPROVEMENTS:
- Initial acceptance test suite
- New rake tasks for building Ubuntu precise and raring base amd64 boxes
## [0.3.2](https://github.com/fgrehm/vagrant-lxc/compare/v0.3.1...v0.3.2) (April 18, 2013)
- Do not display port forwarding message in case no forwarded ports were set
## [0.3.1](https://github.com/fgrehm/vagrant-lxc/compare/v0.3.0...v0.3.1) (April 18, 2013)
- Improved output to match lxc "verbiage"
## [0.3.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.2.0...v0.3.0) (April 10, 2013)
BACKWARDS INCOMPATIBILITIES:
- Boxes `lxc-template` should support a `--tarball` parameter
- `start_opts` config was renamed to `customize`, please check the README for the expected parameters
- V1 boxes are no longer supported
- `target_rootfs_path` is no longer supported, just symlink `/var/lib/lxc` to the desired folder in case you want to point it to another partition
- Removed support for configuring private networks. It will come back at some point in the future but if you need it you should be able to set using `customize 'network.ipv4', '1.2.3.4/24'`
IMPROVEMENTS:
- lxc templates are removed from lxc template dir after container is created
- Treat NFS shared folders as a normal shared folder instead of ignoring it so we can share the same Vagrantfile with VBox environments
- Support for lxc 0.7.5 (tested on Ubuntu 12.04) [#49](https://github.com/fgrehm/vagrant-lxc/issues/49)
- Remove `/tmp` files when packaging quantal64 base box [#48](https://github.com/fgrehm/vagrant-lxc/issues/48)
- Avoid picking the best mirror on quantal64 base box [#38](https://github.com/fgrehm/vagrant-lxc/issues/38)
BUG FIXES:
- Redirect `redir`'s stderr output to `/dev/null` [#51](https://github.com/fgrehm/vagrant-lxc/issues/51)
- Switch from `ifconfig` to `ip` to grab container's IP to avoid localization issues [#50](https://github.com/fgrehm/vagrant-lxc/issues/50)
## [0.2.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.1.1...v0.2.0) (March 30, 2013)
- Experimental box packaging (only tested with Ubuntu 64 base box)
## [0.1.1](https://github.com/fgrehm/vagrant-lxc/compare/v0.1.0...v0.1.1) (March 29, 2013)
- Removed support for development under Vagrant < 1.1
- Removed rsync from base quantal64 box to speed up containers creation [#40](https://github.com/fgrehm/vagrant-lxc/issues/40)
- Containers are now named after project's root dir [#14](https://github.com/fgrehm/vagrant-lxc/issues/14)
- Skip Vagrant's built in SSH redirect
- Allow setting rootfs from Vagrantfile [#30](https://github.com/fgrehm/vagrant-lxc/issues/30)
## [0.1.0](https://github.com/fgrehm/vagrant-lxc/compare/v0.0.3...v0.1.0) (March 27, 2013)
- Support for chef added to base quantal64 box
- Puppet upgraded to 3.1.1 on base quantal64 box
- Port forwarding support added [#6](https://github.com/fgrehm/vagrant-lxc/issues/6)
## Previous
The changelog began with version 0.1.0 so any changes prior to that
can be seen by checking the tagged releases and reading git commit
messages.

View file

@ -1,24 +0,0 @@
### Please read before contributing
* If you have an issue with base boxes, please create it on https://github.com/fgrehm/vagrant-lxc-base-boxes,
this repository is for the Vagrant plugin only.
* Try not to post questions in the issues tracker. I will probably answer you
but I'll most likely close the issue right away and will continue the discussion
with the issue closed. If you have any questions about the plugin, make sure
you go through the [Wiki](https://github.com/fgrehm/vagrant-lxc/wiki) pages
first (specially the [Troubleshooting Section](https://github.com/fgrehm/vagrant-lxc/wiki/Troubleshooting))
and if you still need answers please ask a question on [Stack Overflow](http://stackoverflow.com/questions/tagged/vagrant-lxc)
using the `vagrant` / `lxc` tag on it so that I get notified :)
* Please do a search on the issues tracker before submitting your issue to
check if it was already reported / fixed.
* When reporting a bug, please include **all** information that you can get
about your environment. Things like vagrant / vagrant-lxc / kernel / lxc /
distro versions, the list of plugins you have installed, a [gist](https://gist.github.com/)
with the output of running `VAGRANT_LOG=debug vagrant COMMAND`, the `Vagrantfile`
you are using and / or base box URL are really useful when tracking down what's
going on on your side of the globe and will get bonus points :)
Thanks!

34
Gemfile
View file

@ -1,23 +1,19 @@
source 'https://rubygems.org'
group :development do
gem 'vagrant', git: 'https://github.com/mitchellh/vagrant.git'
gem 'guard'
gem 'guard-rspec'
gem 'rb-inotify'
end
gemspec
group :development, :test do
gem 'rake', '~> 10.4.2'
gem 'rspec', '~> 3.5.0'
gem 'coveralls', '~> 0.7.2', require: (ENV['COVERAGE'] == 'true')
gem 'vagrant-spec', git: 'https://github.com/mitchellh/vagrant-spec.git'
end
# TODO: Group gems
group :plugins do
acceptance = (ENV['ACCEPTANCE'] == 'true')
gem 'vagrant-cachier', git: 'https://github.com/fgrehm/vagrant-cachier.git', require: !acceptance
gem 'vagrant-pristine', git: 'https://github.com/fgrehm/vagrant-pristine.git', require: !acceptance
gem 'vagrant-omnibus', require: !acceptance
gemspec
end
gem 'rake'
# TODO: setup rake-notes
gem 'net-ssh'
gem 'rspec'
gem 'rspec-fire', require: 'rspec/fire'
gem 'rspec-spies', require: false
gem 'simplecov', require: false
gem 'guard'
gem 'guard-rspec'
gem 'guard-bundler'
gem 'guard-ctags-bundler'
gem 'rb-inotify'
gem 'log4r'

89
Gemfile.lock Normal file
View file

@ -0,0 +1,89 @@
PATH
remote: .
specs:
vagrant-lxc (0.0.1)
childprocess (~> 0.3.7)
erubis (~> 2.7.0)
i18n (~> 0.6.0)
json (>= 1.5.1, < 1.8.0)
log4r (~> 1.1.9)
net-scp (~> 1.0.4)
net-ssh (~> 2.2.2)
GEM
remote: https://rubygems.org/
specs:
childprocess (0.3.9)
ffi (~> 1.0, >= 1.0.11)
coderay (1.0.9)
diff-lcs (1.2.1)
erubis (2.7.0)
ffi (1.4.0)
guard (1.6.2)
listen (>= 0.6.0)
lumberjack (>= 1.0.2)
pry (>= 0.9.10)
terminal-table (>= 1.4.3)
thor (>= 0.14.6)
guard-bundler (1.0.0)
bundler (~> 1.0)
guard (~> 1.1)
guard-ctags-bundler (0.1.6)
guard (>= 1.1)
guard-rspec (2.5.0)
guard (>= 1.1)
rspec (~> 2.11)
i18n (0.6.4)
json (1.7.7)
listen (0.7.3)
log4r (1.1.10)
lumberjack (1.0.2)
method_source (0.8.1)
multi_json (1.6.1)
net-scp (1.0.4)
net-ssh (>= 1.99.1)
net-ssh (2.2.2)
pry (0.9.12)
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
rake (10.0.3)
rb-inotify (0.9.0)
ffi (>= 0.5.0)
rspec (2.13.0)
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
rspec-core (2.13.0)
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-fire (1.1.3)
rspec (~> 2.11)
rspec-mocks (2.13.0)
rspec-spies (2.1.3)
rspec (~> 2.0)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
simplecov-html (0.7.1)
slop (3.4.3)
terminal-table (1.4.5)
thor (0.17.0)
PLATFORMS
ruby
DEPENDENCIES
guard
guard-bundler
guard-ctags-bundler
guard-rspec
log4r
net-ssh
rake
rb-inotify
rspec
rspec-fire
rspec-spies
simplecov
vagrant-lxc!

View file

@ -1,7 +1,34 @@
guard 'rspec', :spec_paths => ["spec/unit"] do
watch(%r{^spec/unit/.+_spec\.rb$})
# A sample Guardfile
# More info at https://github.com/guard/guard#readme
guard 'bundler' do
watch('Gemfile')
watch(/^.+\.gemspec/)
end
guard 'ctags-bundler', :src_path => ["lib"] do
watch(/^(lib|spec\/support)\/.*\.rb$/)
watch('Gemfile.lock')
end
guard 'rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/vagrant-lxc/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
watch('spec/unit_helper.rb') { "spec/unit" }
watch('spec/spec_helper.rb') { "spec/unit" }
watch(%r{^spec/support/(.+)\.rb$}) { "spec/unit" }
watch('spec/spec_helper.rb') { "spec/" }
# Rails example
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
watch('config/routes.rb') { "spec/routing" }
watch('app/controllers/application_controller.rb') { "spec/controllers" }
# Capybara features specs
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
# Turnip features and steps
watch(%r{^spec/acceptance/(.+)\.feature$})
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
end

View file

@ -1,4 +1,4 @@
Copyright (c) 2013-2014 Fábio Rehm
Copyright (c) 2013 Fábio Rehm
MIT License

287
README.md
View file

@ -1,186 +1,203 @@
🟢 We plan to support and maintain vagrant-lxc, as well as clean it up.<br/>
🟢 Please feel free to contribute Issues and pull requests.<br/>
🟢 P.S: Thanks [Fabio Rehm](https://fabiorehm.com) for the amazing initial project.
# vagrant-lxc
[![Build Status](https://travis-ci.org/fgrehm/vagrant-lxc.png?branch=master)](https://travis-ci.org/fgrehm/vagrant-lxc) [![Gem Version](https://badge.fury.io/rb/vagrant-lxc.png)](http://badge.fury.io/rb/vagrant-lxc) [![Code Climate](https://codeclimate.com/github/fgrehm/vagrant-lxc.png)](https://codeclimate.com/github/fgrehm/vagrant-lxc) [![Coverage Status](https://coveralls.io/repos/fgrehm/vagrant-lxc/badge.png?branch=master)](https://coveralls.io/r/fgrehm/vagrant-lxc) [![Gitter chat](https://badges.gitter.im/fgrehm/vagrant-lxc.png)](https://gitter.im/fgrehm/vagrant-lxc)
[![Build Status](https://travis-ci.org/fgrehm/vagrant-lxc.png?branch=master)](https://travis-ci.org/fgrehm/vagrant-lxc)
[LXC](http://lxc.sourceforge.net/) provider for [Vagrant](http://www.vagrantup.com/) 1.9+
Highly experimental, soon to come, Linux Containers support for the unreleased
Vagrant 1.1.
This is a Vagrant plugin that allows it to control and provision Linux Containers
as an alternative to the built in VirtualBox provider for Linux hosts. Check out
[this blog post](http://fabiorehm.com/blog/2013/04/28/lxc-provider-for-vagrant/)
to see it in action.
## Features
* Provides the same workflow as the Vagrant VirtualBox provider
* Port forwarding via [`redir`](https://github.com/troglobit/redir)
* Private networking via [`pipework`](https://github.com/jpetazzo/pipework)
## Requirements
* [Vagrant 1.9+](http://www.vagrantup.com/downloads.html)
* lxc >=2.1
* `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`)
The plugin is known to work better and pretty much out of the box on Ubuntu 14.04+
hosts and installing the dependencies on it basically means a
`apt-get install lxc lxc-templates cgroup-lite redir`. For setting up other
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)
blog post for some ideas on how to set things up or check out [this other repo](https://github.com/fgrehm/vagrant-lxc-vbox-hosts)
for a set of Vagrant VirtualBox machines ready for vagrant-lxc usage.
Please refer to the [closed issues](https://github.com/fgrehm/vagrant-lxc/issues?labels=&milestone=&page=1&state=closed)
to find out whats currently supported.
## Installation
## WARNING
Please keep in mind that although I'm already using this on my laptop, this is
"almost alpha" software and things might go wrong.
## Dependencies
LXC, `bsdtar` and `fping` packages and a Kernel [higher than 3.5.0-17.28](#im-unable-to-restart-containers),
which on Ubuntu 12.10 means:
```
vagrant plugin install vagrant-lxc
sudo apt-get update && sudo apt-get dist-upgrade
sudo apt-get install lxc bsdtar fping
```
## Quick start
## What is currently supported?
* Vagrant's `up`, `halt`, `reload`, `destroy`, and `ssh` commands
* Shared folders
* Provisioners
* Setting container's host name
* Host-only / private networking
## Current limitations
* Ruby >= 1.9.3 only, patches for 1.8.7 are welcome
* Port forwarding does not work [yet](https://github.com/fgrehm/vagrant-lxc/issues/4)
* A hell lot of `sudo`s
* Only a [single ubuntu box supported](boxes), I'm still [figuring out what should go
on the .box file](https://github.com/fgrehm/vagrant-lxc/issues/4)
* "[works on my machine](https://github.com/fgrehm/vagrant-lxc/issues/20)" (TM)
* + bunch of other [core features](https://github.com/fgrehm/vagrant-lxc/issues?labels=core&milestone=&page=1&state=open)
and some known [bugs](https://github.com/fgrehm/vagrant-lxc/issues?labels=bug&page=1&state=open)
## Usage
For now you'll need to install the gem from sources:
```
vagrant init fgrehm/precise64-lxc
vagrant up --provider=lxc
git clone git://github.com/fgrehm/vagrant-lxc.git --recurse
cd vagrant-lxc
bundle install
bundle exec rake install
```
_More information about skipping the `--provider` argument can be found at the
"DEFAULT PROVIDER" section of [Vagrant docs](https://docs.vagrantup.com/v2/providers/basic_usage.html)_
## Base boxes
Base boxes provided on Atlas haven't been refreshed for a good while and shouldn't be relied on.
Your best best is to build your boxes yourself. Some scripts to build your own are available at
[hsoft/vagrant-lxc-base-boxes](https://github.com/hsoft/vagrant-lxc-base-boxes).
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.
## Advanced configuration
You can modify container configurations from within your Vagrantfile using the
[provider block](http://docs.vagrantup.com/v2/providers/configuration.html):
Since Vagrant 1.1 has not been released yet and to avoid messing up with you
current Vagrant installation, I've vendored Vagrant's sources from the master
and made it available from [`vagrant-lxc`](bin/vagrant-lxc). So after installing
`vagrant-lxc`, create a `Vagrantfile` like the one below and run
`vagrant-lxc up --provider=lxc`:
```ruby
Vagrant.configure("2") do |config|
config.vm.box = "fgrehm/trusty64-lxc"
config.vm.box = "lxc-quantal64"
config.vm.box_url = 'http://dl.dropbox.com/u/13510779/lxc-quantal64-2013-03-10.box'
# Share an additional folder to the guest Container. The first argument
# is the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
config.vm.synced_folder "/tmp", "/host_tmp"
config.vm.provider :lxc do |lxc|
# Same effect as 'customize ["modifyvm", :id, "--memory", "1024"]' for VirtualBox
lxc.customize 'cgroup.memory.limit_in_bytes', '1024M'
# Same as 'customize ["modifyvm", :id, "--memory", "1024"]' for VirtualBox
lxc.start_opts << 'lxc.cgroup.memory.limit_in_bytes=400M'
# Limits swap size
lxc.start_opts << 'lxc.cgroup.memory.memsw.limit_in_bytes=500M'
end
# ... your puppet / chef / shell provisioner configs here ...
end
```
vagrant-lxc will then write out `lxc.cgroup.memory.limit_in_bytes='1024M'` to the
container config file (usually kept under `/var/lib/lxc/<container>/config`)
prior to starting it.
If you don't trust me and believe that it will mess up with your current Vagrant
installation and / or are afraid that something might go wrong with your machine,
fire up the [same Vagrant VirtualBox machine I'm using for development](#using-virtualbox-and-vagrant-10-for-development)
to try things out and do the same as above. That might also get you up and running
if you are working on a mac or windows host ;)
For other configuration options, please check the [lxc.conf manpages](http://manpages.ubuntu.com/manpages/precise/man5/lxc.conf.5.html).
### Private Networks
## Development
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)
by leveraging the [pipework](https://github.com/jpetazzo/pipework) project.
If want to develop from your physical machine, just sing that same old song:
On its current state, there is a requirement for setting the bridge name that
will be created and will allow your machine to comunicate with the container
For example:
```ruby
Vagrant.configure("2") do |config|
config.vm.network "private_network", ip: "192.168.2.100", lxc__bridge_name: 'vlxcbr1'
end
```
git clone git://github.com/fgrehm/vagrant-lxc.git --recurse
cd vagrant-lxc
bundle install
bundle exec rake # to run all specs
```
Will create a new `veth` device for the container and will set up (or reuse)
a `vlxcbr1` bridge between your machine and the `veth` device. Once the last
vagrant-lxc container attached to the bridge gets `vagrant halt`ed, the plugin
will delete the bridge.
To build the provided quantal64 box:
### Container naming
By default vagrant-lxc will attempt to generate a unique container name
for you. However, if the container name is important to you, you may use the
`container_name` attribute to set it explicitly from the `provider` block:
```ruby
Vagrant.configure("2") do |config|
config.vm.define "db" do |node|
node.vm.provider :lxc do |lxc|
lxc.container_name = :machine # Sets the container name to 'db'
lxc.container_name = 'mysql' # Sets the container name to 'mysql'
end
end
end
```
bundle exec rake boxes:quantal64:build
vagrant-lxc box add quantal64 boxes/output/lxc-quantal64.box
```
_Please note that there is a 64 chars limit and the container name will be
trimmed down to that to ensure we can always bring the container up.
### Backingstore options
### Using `vagrant-lxc` to develop itself
Support for setting `lxc-create`'s backingstore option (`-B` and related) can be
specified from the provider block and it defaults to `best`, to change it:
Yes! The gem has been [bootstrapped](http://en.wikipedia.org/wiki/Bootstrapping_(compilers)
and since you can boot a container from within another, after cloning the
project you can run the commands below from the host machine to get a container
ready for development:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc|
lxc.backingstore = 'lvm' # or 'btrfs', 'overlayfs', ...
# lvm specific options
lxc.backingstore_option '--vgname', 'schroots'
lxc.backingstore_option '--fssize', '5G'
lxc.backingstore_option '--fstype', 'xfs'
end
end
```sh
bundle install
cd development
cp Vagrantfile.1.1 Vagrantfile
# Required in order to allow nested containers to be started
sudo apt-get install apparmor-utils
sudo aa-complain /usr/bin/lxc-start
bundle exec vagrant-lxc up lxc --provider=lxc
bundle exec vagrant-lxc ssh lxc
```
## Unprivileged containers support
That should result in a container ready to be `bundle exec vagrant-lxc ssh`ed.
Once you've SSH into the guest container, you'll be already on the project's root.
Keep in mind that you'll probably need to run `sudo aa-complain /usr/bin/lxc-start`
on the host whenever you want to hack on it, otherwise you won't be able to
start nested containers there to try things out.
Since v1.4.0, `vagrant-lxc` gained support for unprivileged containers. For now, since it's a new
feature, privileged containers are still the default, but you can have your `Vagrantfile` use
unprivileged containers with the `privileged` flag (which defaults to `true`). Example:
```ruby
Vagrant.configure("2") do |config|
config.vm.provider :lxc do |lxc|
lxc.privileged = false
end
end
### Using VirtualBox and Vagrant 1.0 for development
```
cd development
cp Vagrantfile.1.0 Vagrantfile
vagrant up
vagrant reload
vagrant ssh
```
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/).
### Using VirtualBox and Vagrant 1.1 for development
## Avoiding `sudo` passwords
If you're not using unprivileged containers, this plugin requires **a lot** of `sudo`ing To work
around that, you can use the `vagrant lxc sudoers` command which will create a file under
`/etc/sudoers.d/vagrant-lxc` whitelisting all commands required by `vagrant-lxc` to run.
If you are interested on what will be generated by that command, please check
[this code](lib/vagrant-lxc/command/sudoers.rb).
```
cd development
cp Vagrantfile.1.1 Vagrantfile
bundle exec vagrant-lxc up vbox
bundle exec vagrant-lxc reload vbox
bundle exec vagrant-lxc ssh vbox
```
## More information
## Protips
Please refer the [wiki](https://github.com/fgrehm/vagrant-lxc/wiki).
If you want to find out more about what's going on under the hood on vagrant,
prepend `VAGRANT_LOG=debug` to your `vagrant-lxc` commands. For `lxc-start`s
debugging set `LXC_START_LOG_FILE`:
```
LXC_START_LOG_FILE=/tmp/lxc-start.log VAGRANT_LOG=debug vagrant-lxc up
```
This will output A LOT of information on your terminal and some useful information
about `lxc-start` to `/tmp/lxc-start.log`.
## Problems / ideas?
## Help!
Please review the [Troubleshooting](https://github.com/fgrehm/vagrant-lxc/wiki/Troubleshooting)
wiki page + [known bugs](https://github.com/fgrehm/vagrant-lxc/issues?labels=bug&page=1&state=open)
list if you have a problem and feel free to use the [issue tracker](https://github.com/fgrehm/vagrant-lxc/issues)
propose new functionality and / or report bugs.
### I've accidentaly ran `vagrant-lxc` on a Vagrant 1.0 project and I can't use it anymore
That happened to me before so here's how to recover:
```
rm -rf .vagrant
mv .vagrant.v1* .vagrant
```
### I'm unable to restart containers!
It happened to me quite a few times in the past and it seems that it is related
to a bug on linux kernel, so make sure you are using a bug-free kernel
(>= 3.5.0-17.28). More information can be found on:
* https://bugzilla.kernel.org/show_bug.cgi?id=47181
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1021471
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1065434
Sometimes the dev boxes I'm using are not able to `lxc-start` containers
anymore. Most of the times it was an issue with the arguments [I provided](https://github.com/fgrehm/vagrant-lxc/blob/master/example/Vagrantfile#L14-L18)
to it (or a *buggy* kernel). If you run into that, rollback your changes
and try to `vagrant reload` the dev box. If it still doesn't work,
please file a bug at the [issue tracker](https://github.com/fgrehm/vagrant-lxc/issues).
## Contributing

View file

@ -1,3 +1,5 @@
Dir['./tasks/**/*.rake'].each { |f| load f }
require 'bundler/gem_tasks'
task :ci => ['spec:unit']

9
bin/vagrant-lxc Executable file
View file

@ -0,0 +1,9 @@
#!/usr/bin/env ruby
if File.exists?(File.join(File.expand_path('../../', __FILE__), '.git'))
$:.unshift(File.expand_path('../../lib', __FILE__))
end
require 'vagrant-lxc'
load Vagrant.source_root.join('bin/vagrant').to_s

103
boxes/quantal64/download-ubuntu Executable file
View file

@ -0,0 +1,103 @@
#!/bin/bash
# This is the code extracted from /usr/share/lxc/templates/lxc-ubuntu
# that comes with Ubuntu 12.10 which is responsible for downloading the
# rootfs files / packages
set -e
suggest_flush()
{
echo "Container upgrade failed. The container cache may be out of date,"
echo "in which case flushing the case (see -F in the hep output) may help."
}
cleanup()
{
rm -rf $cache/partial-$arch
rm -rf $cache/rootfs-$arch
}
write_sourceslist()
{
# $1 => path to the rootfs
# $2 => architecture we want to add
MIRROR=${MIRROR:-mirror://mirrors.ubuntu.com/mirrors.txt}
SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu}
cat >> "$1/etc/apt/sources.list" << EOF
deb $MIRROR ${release} main restricted universe multiverse
deb $MIRROR ${release}-updates main restricted universe multiverse
deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse
EOF
}
download_ubuntu()
{
packages=vim,ssh,curl,wget,bash-completion,manpages,puppet
echo "installing packages: $packages"
trap cleanup EXIT SIGHUP SIGINT SIGTERM
# check the mini ubuntu was not already downloaded
mkdir -p "$cache/partial-$arch"
if [ $? -ne 0 ]; then
echo "Failed to create '$cache/partial-$arch' directory"
return 1
fi
# download a mini ubuntu into a cache
echo "Downloading ubuntu $release minimal ..."
if [ -n "$(which qemu-debootstrap)" ]; then
qemu-debootstrap --verbose --components=main,universe --arch=$arch --include=$packages $release $cache/partial-$arch $MIRROR
else
debootstrap --verbose --components=main,universe --arch=$arch --include=$packages $release $cache/partial-$arch $MIRROR
fi
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
# Serge isn't sure whether we should avoid doing this when
# $release == `distro-info -d`
echo "Installing updates"
> $cache/partial-$arch/etc/apt/sources.list
write_sourceslist $cache/partial-$arch/ $arch
chroot "$1/partial-${arch}" apt-get update
if [ $? -ne 0 ]; then
echo "Failed to update the apt cache"
return 1
fi
cat > "$1/partial-${arch}"/usr/sbin/policy-rc.d << EOF
#!/bin/sh
exit 101
EOF
chmod +x "$1/partial-${arch}"/usr/sbin/policy-rc.d
lxc-unshare -s MOUNT -- chroot "$1/partial-${arch}" apt-get dist-upgrade -y || { suggest_flush; false; }
rm -f "$1/partial-${arch}"/usr/sbin/policy-rc.d
chroot "$1/partial-${arch}" apt-get clean
mv "$1/partial-$arch" "$1/rootfs-$arch"
trap EXIT
trap SIGINT
trap SIGTERM
trap SIGHUP
echo "Download complete"
return 0
}
declare cache=`readlink -f .` \
arch=amd64 \
release=quantal
if [ -d "${cache}/rootfs-${arch}" ]; then
echo 'The rootfs cache has been downloaded already, please remove it if you want to update'
exit 1
fi
download_ubuntu $cache $arch $release

567
boxes/quantal64/lxc-template Executable file
View file

@ -0,0 +1,567 @@
#!/bin/bash
# This is a modified version of /usr/share/lxc/templates/lxc-ubuntu
# that comes with Ubuntu 12.10 changed to suit vagrant-lxc needs
#
# template script for generating ubuntu container for LXC
#
# This script consolidates and extends the existing lxc ubuntu scripts
#
# Copyright © 2011 Serge Hallyn <serge.hallyn@canonical.com>
# Copyright © 2010 Wilhelm Meier
# Author: Wilhelm Meier <wilhelm.meier@fh-kl.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2, as
# published by the Free Software Foundation.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
set -e
if [ -r /etc/default/lxc ]; then
. /etc/default/lxc
fi
configure_ubuntu()
{
rootfs=$1
release=$3
hostname='quantal64'
# configure the network using the dhcp
cat <<EOF > $rootfs/etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
EOF
# set the hostname
cat <<EOF > $rootfs/etc/hostname
$hostname
EOF
# set minimal hosts
cat <<EOF > $rootfs/etc/hosts
127.0.0.1 localhost
127.0.1.1 $hostname
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
if [ ! -f $rootfs/etc/init/container-detect.conf ]; then
# suppress log level output for udev
sed -i "s/=\"err\"/=0/" $rootfs/etc/udev/udev.conf
# remove jobs for consoles 5 and 6 since we only create 4 consoles in
# this template
rm -f $rootfs/etc/init/tty{5,6}.conf
fi
chroot $rootfs useradd --create-home -s /bin/bash vagrant
echo "vagrant:vagrant" | chroot $rootfs chpasswd
return 0
}
# finish setting up the user in the container by injecting ssh key and
# adding sudo group membership.
# passed-in user is 'vagrant'
finalize_user()
{
user=$1
sudo_version=$(chroot $rootfs dpkg-query -W -f='${Version}' sudo)
if chroot $rootfs dpkg --compare-versions $sudo_version gt "1.8.3p1-1"; then
groups="sudo"
else
groups="sudo admin"
fi
for group in $groups; do
chroot $rootfs groupadd --system $group >/dev/null 2>&1 || true
chroot $rootfs adduser ${user} $group >/dev/null 2>&1 || true
done
chroot $rootfs cp /etc/sudoers /etc/sudoers.orig >/dev/null 2>&1 || true
chroot $rootfs sed -i -e 's/%sudo\s\+ALL=(ALL:ALL)\s\+ALL/%sudo ALL=NOPASSWD:ALL/g' /etc/sudoers >/dev/null 2>&1 || true
chroot $rootfs locale-gen en_US en_US.UTF-8 hu_HU hu_HU.UTF-8 >/dev/null 2>&1 || true
chroot $rootfs dpkg-reconfigure locales >/dev/null 2>&1 || true
if [ -n "$auth_key" -a -f "$auth_key" ]; then
u_path="/home/${user}/.ssh"
root_u_path="$rootfs/$u_path"
mkdir -p $root_u_path
cp $auth_key "$root_u_path/authorized_keys"
chroot $rootfs chown -R ${user}: "$u_path"
echo "Inserted SSH public key from $auth_key into /home/${user}/.ssh/authorized_keys"
fi
return 0
}
write_sourceslist()
{
# $1 => path to the rootfs
# $2 => architecture we want to add
# $3 => whether to use the multi-arch syntax or not
case $2 in
amd64|i386)
MIRROR=${MIRROR:-http://archive.ubuntu.com/ubuntu}
SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.ubuntu.com/ubuntu}
;;
*)
MIRROR=${MIRROR:-http://ports.ubuntu.com/ubuntu-ports}
SECURITY_MIRROR=${SECURITY_MIRROR:-http://ports.ubuntu.com/ubuntu-ports}
;;
esac
if [ -n "$3" ]; then
cat >> "$1/etc/apt/sources.list" << EOF
deb [arch=$2] $MIRROR ${release} main restricted universe multiverse
deb [arch=$2] $MIRROR ${release}-updates main restricted universe multiverse
deb [arch=$2] $SECURITY_MIRROR ${release}-security main restricted universe multiverse
EOF
else
cat >> "$1/etc/apt/sources.list" << EOF
deb $MIRROR ${release} main restricted universe multiverse
deb $MIRROR ${release}-updates main restricted universe multiverse
deb $SECURITY_MIRROR ${release}-security main restricted universe multiverse
EOF
fi
}
copy_ubuntu()
{
cache=$1
arch=$2
rootfs=$3
# make a local copy of the miniubuntu
echo "Copying rootfs to $rootfs ..."
mkdir -p $rootfs
rsync -a $cache/rootfs-$arch/ $rootfs/ || return 1
return 0
}
install_ubuntu()
{
rootfs=$1
release=$2
cache=$3 # "/var/cache/lxc/$release"
mkdir -p /var/lock/subsys/
(
flock -x 200
if [ $? -ne 0 ]; then
echo "Cache repository is busy."
return 1
fi
echo "Copy $cache/rootfs-$arch to $rootfs ... "
copy_ubuntu $cache $arch $rootfs
if [ $? -ne 0 ]; then
echo "Failed to copy rootfs"
return 1
fi
return 0
) 200>/var/lock/subsys/lxc
return $?
}
copy_configuration()
{
path=$1
rootfs=$2
name=$3
arch=$4
release=$5
if [ $arch = "i386" ]; then
arch="i686"
fi
ttydir=""
if [ -f $rootfs/etc/init/container-detect.conf ]; then
ttydir=" lxc"
fi
# if there is exactly one veth network entry, make sure it has an
# associated hwaddr.
nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l`
if [ $nics -eq 1 ]; then
grep -q "^lxc.network.hwaddr" $path/config || cat <<EOF >> $path/config
lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')
EOF
fi
grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
cat <<EOF >> $path/config
lxc.utsname = $name
lxc.devttydir =$ttydir
lxc.tty = 4
lxc.pts = 1024
lxc.mount = $path/fstab
lxc.arch = $arch
lxc.cap.drop = sys_module mac_admin mac_override
lxc.pivotdir = lxc_putold
# uncomment the next line to run the container unconfined:
#lxc.aa_profile = unconfined
lxc.cgroup.devices.deny = a
# Allow any mknod (but not using the node)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
#lxc.cgroup.devices.allow = c 4:0 rwm
#lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
#fuse
lxc.cgroup.devices.allow = c 10:229 rwm
#tun
lxc.cgroup.devices.allow = c 10:200 rwm
#full
lxc.cgroup.devices.allow = c 1:7 rwm
#hpet
lxc.cgroup.devices.allow = c 10:228 rwm
#kvm
lxc.cgroup.devices.allow = c 10:232 rwm
EOF
cat <<EOF > $path/fstab
proc proc proc nodev,noexec,nosuid 0 0
sysfs sys sysfs defaults 0 0
EOF
if [ $? -ne 0 ]; then
echo "Failed to add configuration"
return 1
fi
return 0
}
trim()
{
rootfs=$1
release=$2
# provide the lxc service
cat <<EOF > $rootfs/etc/init/lxc.conf
# fake some events needed for correct startup other services
description "Container Upstart"
start on startup
script
rm -rf /var/run/*.pid
rm -rf /var/run/network/*
/sbin/initctl emit stopped JOB=udevtrigger --no-wait
/sbin/initctl emit started JOB=udev --no-wait
end script
EOF
# fix buggus runlevel with sshd
cat <<EOF > $rootfs/etc/init/ssh.conf
# ssh - OpenBSD Secure Shell server
#
# The OpenSSH server provides secure shell access to the system.
description "OpenSSH server"
start on filesystem
stop on runlevel [!2345]
expect fork
respawn
respawn limit 10 5
umask 022
# replaces SSHD_OOM_ADJUST in /etc/default/ssh
oom never
pre-start script
test -x /usr/sbin/sshd || { stop; exit 0; }
test -e /etc/ssh/sshd_not_to_be_run && { stop; exit 0; }
test -c /dev/null || { stop; exit 0; }
mkdir -p -m0755 /var/run/sshd
end script
# if you used to set SSHD_OPTS in /etc/default/ssh, you can change the
# 'exec' line here instead
exec /usr/sbin/sshd
EOF
cat <<EOF > $rootfs/etc/init/console.conf
# console - getty
#
# This service maintains a console on tty1 from the point the system is
# started until it is shut down again.
start on stopped rc RUNLEVEL=[2345]
stop on runlevel [!2345]
respawn
exec /sbin/getty -8 38400 /dev/console
EOF
cat <<EOF > $rootfs/lib/init/fstab
# /lib/init/fstab: cleared out for bare-bones lxc
EOF
# reconfigure some services
if [ -z "$LANG" ]; then
chroot $rootfs locale-gen en_US.UTF-8
chroot $rootfs update-locale LANG=en_US.UTF-8
else
chroot $rootfs locale-gen $LANG
chroot $rootfs update-locale LANG=$LANG
fi
# remove pointless services in a container
chroot $rootfs /usr/sbin/update-rc.d -f ondemand remove
chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls u*.conf); do mv $f $f.orig; done'
chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls tty[2-9].conf); do mv $f $f.orig; done'
chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls plymouth*.conf); do mv $f $f.orig; done'
chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls hwclock*.conf); do mv $f $f.orig; done'
chroot $rootfs /bin/bash -c 'cd /etc/init; for f in $(ls module*.conf); do mv $f $f.orig; done'
# if this isn't lucid, then we need to twiddle the network upstart bits :(
if [ $release != "lucid" ]; then
sed -i 's/^.*emission handled.*$/echo Emitting lo/' $rootfs/etc/network/if-up.d/upstart
fi
}
post_process()
{
rootfs=$1
release=$2
trim_container=$3
if [ $trim_container -eq 1 ]; then
trim $rootfs $release
elif [ ! -f $rootfs/etc/init/container-detect.conf ]; then
# Make sure we have a working resolv.conf
cresolvonf="${rootfs}/etc/resolv.conf"
mv $cresolvonf ${cresolvonf}.lxcbak
cat /etc/resolv.conf > ${cresolvonf}
# for lucid, if not trimming, then add the ubuntu-virt
# ppa and install lxcguest
if [ $release = "lucid" ]; then
chroot $rootfs apt-get update
chroot $rootfs apt-get install --force-yes -y python-software-properties
chroot $rootfs add-apt-repository ppa:ubuntu-virt/ppa
fi
chroot $rootfs apt-get update
chroot $rootfs apt-get install --force-yes -y lxcguest
# Restore old resolv.conf
rm -f ${cresolvonf}
mv ${cresolvonf}.lxcbak ${cresolvonf}
fi
# If the container isn't running a native architecture, setup multiarch
if [ -x "$(ls -1 ${rootfs}/usr/bin/qemu-*-static 2>/dev/null)" ]; then
dpkg_version=$(chroot $rootfs dpkg-query -W -f='${Version}' dpkg)
if chroot $rootfs dpkg --compare-versions $dpkg_version ge "1.16.2"; then
chroot $rootfs dpkg --add-architecture ${hostarch}
else
mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d
echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch
fi
# Save existing value of MIRROR and SECURITY_MIRROR
DEFAULT_MIRROR=$MIRROR
DEFAULT_SECURITY_MIRROR=$SECURITY_MIRROR
# Write a new sources.list containing both native and multiarch entries
> ${rootfs}/etc/apt/sources.list
write_sourceslist $rootfs $arch "native"
MIRROR=$DEFAULT_MIRROR
SECURITY_MIRROR=$DEFAULT_SECURITY_MIRROR
write_sourceslist $rootfs $hostarch "multiarch"
# Finally update the lists and install upstart using the host architecture
chroot $rootfs apt-get update
chroot $rootfs apt-get install --force-yes -y --no-install-recommends upstart:${hostarch} mountall:${hostarch} iproute:${hostarch} isc-dhcp-client:${hostarch}
fi
# rmdir /dev/shm for containers that have /run/shm
# I'm afraid of doing rm -rf $rootfs/dev/shm, in case it did
# get bind mounted to the host's /run/shm. So try to rmdir
# it, and in case that fails move it out of the way.
if [ ! -L $rootfs/dev/shm ] && [ -d $rootfs/run/shm ] && [ -e $rootfs/dev/shm ]; then
mv $rootfs/dev/shm $rootfs/dev/shm.bak
ln -s /run/shm $rootfs/dev/shm
fi
}
usage()
{
cat <<EOF
$1 -h|--help [-a|--arch] [--trim] [-d|--debug]
[-F | --flush-cache] [-r|--release <release>] [ -S | --auth-key <keyfile>]
release: the ubuntu release (e.g. precise): defaults to host release on ubuntu, otherwise uses latest LTS
trim: make a minimal (faster, but not upgrade-safe) container
arch: the container architecture (e.g. amd64): defaults to host arch
auth-key: SSH Public key file to inject into container
EOF
return 0
}
options=$(getopt -o a:b:hp:r:xn:FS:d:C -l arch:,help,path:,release:,trim,name:,flush-cache,auth-key:,debug:,cache: -- "$@")
if [ $? -ne 0 ]; then
usage $(basename $0)
exit 1
fi
eval set -- "$options"
release=precise # Default to the last Ubuntu LTS release for non-Ubuntu systems
if [ -f /etc/lsb-release ]; then
. /etc/lsb-release
if [ "$DISTRIB_ID" = "Ubuntu" ]; then
release=$DISTRIB_CODENAME
fi
fi
arch=$(arch)
# Code taken from debootstrap
if [ -x /usr/bin/dpkg ] && /usr/bin/dpkg --print-architecture >/dev/null 2>&1; then
arch=`/usr/bin/dpkg --print-architecture`
elif type udpkg >/dev/null 2>&1 && udpkg --print-architecture >/dev/null 2>&1; then
arch=`/usr/bin/udpkg --print-architecture`
else
arch=$(arch)
if [ "$arch" = "i686" ]; then
arch="i386"
elif [ "$arch" = "x86_64" ]; then
arch="amd64"
elif [ "$arch" = "armv7l" ]; then
arch="armel"
fi
fi
debug=0
trim_container=0
hostarch=$arch
while true
do
case "$1" in
-h|--help) usage $0 && exit 0;;
-p|--path) path=$2; shift 2;;
-n|--name) name=$2; shift 2;;
-C|--cache) cache=$2; shift 2;;
-r|--release) release=$2; shift 2;;
-a|--arch) arch=$2; shift 2;;
-x|--trim) trim_container=1; shift 1;;
-S|--auth-key) auth_key=$2; shift 2;;
-d|--debug) debug=1; shift 1;;
--) shift 1; break ;;
*) break ;;
esac
done
if [ $debug -eq 1 ]; then
set -x
fi
if [ "$arch" == "i686" ]; then
arch=i386
fi
if [ $hostarch = "i386" -a $arch = "amd64" ]; then
echo "can't create amd64 container on i386"
exit 1
fi
if [ -z "$path" ]; then
echo "'path' parameter is required"
exit 1
fi
if [ "$(id -u)" != "0" ]; then
echo "This script should be run as 'root'"
exit 1
fi
# detect rootfs
config="$path/config"
if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
rootfs=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'`
else
rootfs=$path/rootfs
fi
install_ubuntu $rootfs $release $cache
if [ $? -ne 0 ]; then
echo "failed to install ubuntu $release"
exit 1
fi
configure_ubuntu $rootfs $release
if [ $? -ne 0 ]; then
echo "failed to configure ubuntu $release for a container"
exit 1
fi
copy_configuration $path $rootfs $name $arch $release
if [ $? -ne 0 ]; then
echo "failed write configuration file"
exit 1
fi
post_process $rootfs $release $trim_container
finalize_user vagrant
echo ""
echo "##"
echo "# The default user is 'vagrant' with password 'vagrant'!"
echo "# Use the 'sudo' command to run tasks as root in the container."
echo "##"
echo ""

View file

@ -0,0 +1,9 @@
{
"provider": "lxc",
"vagrant-lxc-version": "0.0.1",
"template-opts": {
"--arch": "amd64",
"--release": "quantal"
}
}

View file

@ -0,0 +1,35 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
def local_cache(box_name)
cache_dir = File.join(File.expand_path(Vagrant::Environment::DEFAULT_HOME),
'cache',
'apt',
box_name)
partial_dir = File.join(cache_dir, 'partial')
FileUtils.mkdir_p(partial_dir) unless File.exists? partial_dir
cache_dir
end
Vagrant::Config.run do |config|
config.vm.box = "quantal64"
config.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box"
cache_dir = local_cache(config.vm.box)
config.vm.share_folder "v-root", "/vagrant", "../"
config.vm.share_folder "v-cache", "/var/cache/apt/archives", cache_dir
if defined? VagrantVbguest::Config
config.vbguest.auto_update = false
config.vbguest.no_remote = true
end
config.vm.provision :shell, :path => 'shell-provisioning/upgrade-kernel'
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "."
puppet.manifest_file = "site.pp"
# Pass DEBUG=1 to vagrant commands if you want to make some debugging noise
puppet.options << [ "--verbose", "--debug" ] if ENV["DEBUG"] == '1'
end
end

View file

@ -0,0 +1,55 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
def local_cache(box_name)
cache_dir = File.join(File.expand_path(Vagrant::Environment::DEFAULT_HOME),
'cache',
'apt',
box_name)
partial_dir = File.join(cache_dir, 'partial')
FileUtils.mkdir_p(partial_dir) unless File.exists? partial_dir
cache_dir
end
Vagrant.require_plugin 'vagrant-lxc'
Vagrant.configure("2") do |config|
config.vm.box = "quantal64"
config.vm.synced_folder "../", "/vagrant", name: 'vagrant-root'
cache_dir = local_cache(config.vm.box)
config.vm.synced_folder cache_dir, "/var/cache/apt/archives", name: "vagrant-cache"
config.vm.define :vbox do |vb_config|
vb_config.vm.box_url = "https://github.com/downloads/roderik/VagrantQuantal64Box/quantal64.box"
vb_config.vm.hostname = 'vbox'
vb_config.vm.provider :virtualbox do |vb|
# Configure VM to use 1.5gb of ram and 3 cpus
vb.customize [
"modifyvm", :id,
"--memory", '1536',
"--cpus", '4'
]
end
vb_config.vm.provision :shell, :path => 'shell-provisioning/upgrade-kernel'
end
config.vm.define :lxc do |lxc_config|
lxc_config.vm.hostname = 'lxc-dev-box'
lxc_config.vm.box_url = 'http://dl.dropbox.com/u/13510779/lxc-quantal64-2013-03-10.box'
lxc_config.vm.provider :lxc do |lxc|
# Required to boot nested containers
lxc.start_opts << 'lxc.aa_profile=unconfined'
end
end
config.vm.provision :puppet do |puppet|
puppet.manifests_path = "."
puppet.manifest_file = "site.pp"
puppet.options << [ '--verbose', '--debug' ]
end
end

View file

@ -0,0 +1,37 @@
###############################################################################
# This file has the same configs as the built in /etc/default/lxc on Ubuntu,
# we only changed IPs to 10.0.254.* to avoid collision with LXC default 10.0.3.*
# which is likely to be running from the host machine
###############################################################################
# MIRROR to be used by ubuntu template at container creation:
# Leaving it undefined is fine
#MIRROR="http://archive.ubuntu.com/ubuntu"
# or
#MIRROR="http://<host-ip-addr>:3142/archive.ubuntu.com/ubuntu"
# LXC_AUTO - whether or not to start containers symlinked under
# /etc/lxc/auto
LXC_AUTO="true"
# Leave USE_LXC_BRIDGE as "true" if you want to use lxcbr0 for your
# containers. Set to "false" if you'll use virbr0 or another existing
# bridge, or mavlan to your host's NIC.
USE_LXC_BRIDGE="true"
# If you change the LXC_BRIDGE to something other than lxcbr1, then
# you will also need to update your /etc/lxc/lxc.conf as well as the
# configuration (/var/lib/lxc/<container>/config) for any containers
# already created using the default config to reflect the new bridge
# name.
# If you have the dnsmasq daemon installed, you'll also have to update
# /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon.
LXC_BRIDGE="lxcbr0"
LXC_ADDR="10.0.253.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="10.0.253.0/24"
LXC_DHCP_RANGE="10.0.253.2,10.0.253.254"
LXC_DHCP_MAX="253"
LXC_SHUTDOWN_TIMEOUT=120

View file

@ -0,0 +1,37 @@
###############################################################################
# This file has the same configs as the built in /etc/default/lxc on Ubuntu,
# we only changed IPs to 10.0.254.* to avoid collision with LXC default 10.0.3.*
# which is likely to be running from the host machine
###############################################################################
# MIRROR to be used by ubuntu template at container creation:
# Leaving it undefined is fine
#MIRROR="http://archive.ubuntu.com/ubuntu"
# or
#MIRROR="http://<host-ip-addr>:3142/archive.ubuntu.com/ubuntu"
# LXC_AUTO - whether or not to start containers symlinked under
# /etc/lxc/auto
LXC_AUTO="true"
# Leave USE_LXC_BRIDGE as "true" if you want to use lxcbr0 for your
# containers. Set to "false" if you'll use virbr0 or another existing
# bridge, or mavlan to your host's NIC.
USE_LXC_BRIDGE="true"
# If you change the LXC_BRIDGE to something other than lxcbr1, then
# you will also need to update your /etc/lxc/lxc.conf as well as the
# configuration (/var/lib/lxc/<container>/config) for any containers
# already created using the default config to reflect the new bridge
# name.
# If you have the dnsmasq daemon installed, you'll also have to update
# /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon.
LXC_BRIDGE="lxcbr0"
LXC_ADDR="10.0.254.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="10.0.254.0/24"
LXC_DHCP_RANGE="10.0.254.2,10.0.254.254"
LXC_DHCP_MAX="253"
LXC_SHUTDOWN_TIMEOUT=120

View file

@ -0,0 +1,14 @@
#!/bin/bash
set -e
if [[ `uname -r` != "3.5.0-17-generic" ]]; then
exit 0
fi
echo 'An old kernel was found on the guest machine and it will be upgraded' 1>&2
echo 'Please reload the box after provisioning when finished' 1>&2
sudo apt-get update
sudo apt-get install linux-image-3.5.0-25-generic linux-headers-3.5.0-25-generic -y
sudo apt-get dist-upgrade -y
sudo apt-get autoremove -y

125
development/site.pp Normal file
View file

@ -0,0 +1,125 @@
Exec { path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/', '/usr/local/bin'] }
# Because I'm lazy ;)
exec {
'echo "alias be=\"bundle exec\"" >> /home/vagrant/.bashrc':
unless => 'grep -q "bundle exec" /home/vagrant/.bashrc';
'echo "cd /vagrant" >> /home/vagrant/.bashrc':
unless => 'grep -q "cd /vagrant" /home/vagrant/.bashrc';
}
# Overwrite LXC default configs
exec {
'config-lxc':
# We need to do this otherwise IPs will collide with the host's lxc dhcp server.
# If we install the package prior to setting this configs the container will go crazy.
command => "cp /vagrant/development/lxc-configs/${hostname} /etc/default/lxc"
;
}
# Install dependencies
package {
[ 'libffi-dev', 'bsdtar', 'exuberant-ctags', 'ruby1.9.1-dev', 'htop', 'git',
'build-essential', 'dnsutils', 'fping' ]:
ensure => 'installed'
;
'lxc':
require => Exec['config-lxc']
;
'bundler':
ensure => 'installed',
provider => 'gem'
;
}
# Make sure we can create and boot nested containers
if $hostname == 'vbox' {
package { 'apparmor-utils': }
exec { 'aa-complain /usr/bin/lxc-start': }
}
# TMUX
package {
'tmux': ensure => 'installed';
}
# Allow gems to be installed on vagrant user home avoiding "sudo"s
# Tks to http://wiki.railsplayground.com/railsplayground/show/How+to+install+gems+and+non+root+user
file {
'/home/vagrant/gems':
ensure => directory,
owner => 'vagrant',
group => 'vagrant'
;
'/home/vagrant/.gemrc':
content => '
---
:verbose: true
gem: --no-ri --no-rdoc
:update_sources: true
:sources:
- http://gems.rubyforge.org
- http://gems.github.com
:backtrace: false
:bulk_threshold: 1000
:benchmark: false
gemhome: /home/vagrant/gems
gempath:
- /home/vagrant/gems
- /usr/local/lib/ruby/gems/1.8
'
}
exec {
'set-gem-paths':
command => 'cat << EOF >> /home/vagrant/.profile
export GEM_HOME=/home/vagrant/gems
export GEM_PATH=/home/vagrant/gems:/var/lib/gems/1.9.1
export PATH=$PATH:/home/vagrant/gems/bin
EOF',
unless => 'grep -q "GEM_HOME" /home/vagrant/.profile'
}
# Bundle!
exec {
'su -l vagrant -c "cd /vagrant && bundle install"':
# We are checking for guard-rspec here but it could be any gem...
unless => 'gem list guard | grep -q rspec',
cwd => '/vagrant',
require => [
Exec['set-gem-paths'],
File['/home/vagrant/gems', '/home/vagrant/.gemrc'],
Package['bundler']
]
}
# Setup vagrant default ssh key
file {
'/home/vagrant/.ssh':
ensure => directory,
owner => 'vagrant',
group => 'vagrant'
}
exec {
'download-private-key':
command => 'wget https://raw.github.com/mitchellh/vagrant/master/keys/vagrant -O /home/vagrant/.ssh/id_rsa',
creates => '/home/vagrant/.ssh/id_rsa',
require => File['/home/vagrant/.ssh'],
user => 'vagrant'
;
'wget https://raw.github.com/mitchellh/vagrant/master/keys/vagrant.pub -O /home/vagrant/.ssh/id_rsa.pub':
creates => '/home/vagrant/.ssh/id_rsa.pub',
require => File['/home/vagrant/.ssh'],
user => 'vagrant'
;
}
file {
'/home/vagrant/.ssh/id_rsa':
ensure => 'present',
mode => '0600',
require => Exec['download-private-key']
}

42
example/Vagrantfile vendored Normal file
View file

@ -0,0 +1,42 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
def local_cache(box_name)
cache_dir = File.join(File.expand_path(Vagrant::Environment::DEFAULT_HOME),
'cache',
'apt',
box_name)
partial_dir = File.join(cache_dir, 'partial')
FileUtils.mkdir_p(partial_dir) unless File.exists? partial_dir
cache_dir
end
Vagrant.require_plugin 'vagrant-lxc'
Vagrant.configure("2") do |config|
config.vm.box = "quantal64"
config.vm.box_url = 'http://dl.dropbox.com/u/13510779/lxc-quantal64-2013-03-10.box'
config.vm.hostname = 'lxc-quantal64'
config.vm.synced_folder "/tmp", "/vagrant_data"
cache_dir = local_cache(config.vm.box)
config.vm.synced_folder cache_dir, "/var/cache/apt/archives"
config.vm.provider :lxc do |lxc|
lxc.start_opts << 'lxc.cgroup.memory.limit_in_bytes=400M'
lxc.start_opts << 'lxc.cgroup.memory.memsw.limit_in_bytes=500M'
end
config.vm.provision :shell, :inline => <<-SCRIPT
echo "Hi there I'm a shell script used for provisioning"
SCRIPT
config.vm.provision :puppet do |puppet|
puppet.module_path = "puppet/modules"
puppet.manifests_path = "puppet/manifests"
puppet.manifest_file = "site.pp"
# If you want to make some debugging noise
# puppet.options << [ '--verbose', '--debug' ]
end
end

View file

@ -0,0 +1,5 @@
Exec { path => [ '/bin/', '/sbin/' , '/usr/bin/', '/usr/sbin/'] }
notice "Hi there! puppet here"
require hello_world

View file

@ -0,0 +1,3 @@
class hello_world {
notice "Puppet module here. Things seem to be working just fine ;-)"
}

View file

@ -1,10 +1,6 @@
require "vendored_vagrant"
require "vagrant-lxc/version"
require "vagrant-lxc/plugin"
module Vagrant
module LXC
def self.source_root
@source_root ||= Pathname.new(File.dirname(__FILE__)).join('..').expand_path
end
end
end
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../locales/en.yml')

View file

@ -1,82 +1,83 @@
require 'vagrant-lxc/action/base_action'
require 'vagrant-lxc/action/boot'
require 'vagrant-lxc/action/clear_forwarded_ports'
require 'vagrant-lxc/action/check_created'
require 'vagrant-lxc/action/check_running'
require 'vagrant-lxc/action/create'
require 'vagrant-lxc/action/created'
require 'vagrant-lxc/action/destroy'
require 'vagrant-lxc/action/destroy_confirm'
require 'vagrant-lxc/action/compress_rootfs'
require 'vagrant-lxc/action/fetch_ip_with_lxc_info'
require 'vagrant-lxc/action/forced_halt'
require 'vagrant-lxc/action/forward_ports'
require 'vagrant-lxc/action/gc_private_network_bridges'
require 'vagrant-lxc/action/handle_box_metadata'
require 'vagrant-lxc/action/prepare_nfs_settings'
require 'vagrant-lxc/action/prepare_nfs_valid_ids'
require 'vagrant-lxc/action/private_networks'
require 'vagrant-lxc/action/setup_package_files'
require 'vagrant-lxc/action/warn_networks'
require 'vagrant-lxc/action/is_running'
require 'vagrant-lxc/action/network'
require 'vagrant-lxc/action/share_folders'
module Vagrant
module LXC
module Action
# Shortcuts
Builtin = Vagrant::Action::Builtin
Builder = Vagrant::Action::Builder
# This action is responsible for reloading the machine, which
# brings it down, sucks in new configuration, and brings the
# machine back up with the new configuration.
def self.action_reload
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
if env1[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::Call, Created do |env1, b2|
if !env1[:result]
b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated
next
end
b2.use Builtin::ConfigValidate
b2.use Vagrant::Action::Builtin::ConfigValidate
b2.use action_halt
b2.use action_start
end
end
end
# We could do this here as VirtualBox does, but at least for now its better
# to be explicit and have the full constant name in order to easily spot
# what we implemented and what is builtin on Vagrant.
#
# include Vagrant::Action::Builtin
# This action boots the VM, assuming the VM is in a state that requires
# a bootup (i.e. not saved).
def self.action_boot
Builder.new.tap do |b|
b.use Builtin::Provision
b.use Builtin::EnvSet, :port_collision_repair => true
b.use Builtin::HandleForwardedPortCollisions
b.use PrepareNFSValidIds
b.use Builtin::SyncedFolderCleanup
b.use Builtin::SyncedFolders
b.use PrepareNFSSettings
b.use Builtin::SetHostname
b.use WarnNetworks
b.use ForwardPorts
b.use PrivateNetworks
Vagrant::Action::Builder.new.tap do |b|
# b.use ClearForwardedPorts
b.use Vagrant::Action::Builtin::Provision
b.use Vagrant::Action::Builtin::EnvSet, :port_collision_repair => true
# b.use PrepareForwardedPortCollisionParams
# b.use ClearSharedFolders
b.use ShareFolders
b.use Network
# b.use ForwardPorts
b.use Vagrant::Action::Builtin::SetHostname
# b.use SaneDefaults
# b.use Customize
b.use Boot
b.use Builtin::WaitForCommunicator
end
end
# This action just runs the provisioners on the machine.
def self.action_provision
Builder.new.tap do |b|
b.use Builtin::ConfigValidate
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
if env1[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::ConfigValidate
b.use Vagrant::Action::Builtin::Call, Created do |env1, b2|
if !env1[:result]
b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated
next
end
b2.use Builtin::Call, Builtin::IsState, :running do |env2, b3|
b2.use Vagrant::Action::Builtin::Call, IsRunning do |env2, b3|
if !env2[:result]
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_running")
b3.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotRunning
next
end
b3.use Builtin::Provision
# b3.use CheckAccessible
b3.use Vagrant::Action::Builtin::Provision
end
end
end
@ -85,12 +86,13 @@ module Vagrant
# This action starts a container, assuming it is already created and exists.
# A precondition of this action is that the container exists.
def self.action_start
Builder.new.tap do |b|
b.use Builtin::ConfigValidate
b.use Builtin::BoxCheckOutdated
b.use Builtin::Call, Builtin::IsState, :running do |env, b2|
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::ConfigValidate
b.use Vagrant::Action::Builtin::Call, IsRunning do |env, b2|
# If the VM is running, then our work here is done, exit
next if env[:result]
# TODO: Check if has been saved / frozen and resume
b2.use action_boot
end
end
@ -99,12 +101,13 @@ module Vagrant
# This action brings the machine up from nothing, including creating the
# container, configuring metadata, and booting.
def self.action_up
Builder.new.tap do |b|
b.use Builtin::ConfigValidate
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::ConfigValidate
b.use Vagrant::Action::Builtin::Call, Created do |env, b2|
# If the VM is NOT created yet, then do the setup steps
if env[:result]
b2.use Builtin::HandleBox
if !env[:result]
b2.use Vagrant::Action::Builtin::HandleBoxUrl
b2.use HandleBoxMetadata
b2.use Create
end
@ -116,19 +119,17 @@ module Vagrant
# This is the action that is primarily responsible for halting
# the virtual machine, gracefully or by force.
def self.action_halt
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::Call, Created do |env, b2|
if env[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
next
end
b2.use ClearForwardedPorts
b2.use GcPrivateNetworkBridges
b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result]
b3.use ForcedHalt
b2.use Vagrant::Action::Builtin::Call, Vagrant::Action::Builtin::GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result] && env2[:machine].provider.state.running?
b3.use ForcedHalt
end
end
else
b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated
end
end
end
@ -137,96 +138,49 @@ module Vagrant
# This is the action that is primarily responsible for completely
# freeing the resources of the underlying virtual machine.
def self.action_destroy
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
if env1[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use Vagrant::Action::Builtin::Call, Created do |env1, b2|
if !env1[:result]
b2.use VagrantPlugins::ProviderVirtualBox::Action::MessageNotCreated
next
end
b2.use Builtin::Call, DestroyConfirm do |env2, b3|
# TODO: Implement our own DestroyConfirm
b2.use Vagrant::Action::Builtin::Call, VagrantPlugins::ProviderVirtualBox::Action::DestroyConfirm do |env2, b3|
if env2[:result]
b3.use Builtin::ConfigValidate
b3.use Builtin::EnvSet, :force_halt => true
b3.use Vagrant::Action::Builtin::ConfigValidate
b3.use Vagrant::Action::Builtin::EnvSet, :force_halt => true
b3.use action_halt
b3.use Destroy
b3.use Builtin::ProvisionerCleanup
else
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.will_not_destroy")
# TODO: Implement our own MessageWillNotDestroy
b3.use VagrantPlugins::ProviderVirtualBox::Action::MessageWillNotDestroy
end
end
end
end
end
# This action packages the virtual machine into a single box file.
def self.action_package
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::IsState, :not_created do |env1, b2|
if env1[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
next
end
b2.use action_halt
b2.use CompressRootFS
b2.use SetupPackageFiles
b2.use Vagrant::Action::General::Package
end
end
end
# This action is called to read the IP of the container. The IP found
# is expected to be put into the `:machine_ip` key.
def self.action_ssh_ip
Builder.new.tap do |b|
b.use Builtin::Call, Builtin::ConfigValidate do |env, b2|
b2.use FetchIpWithLxcInfo
end
end
end
# This is the action that will exec into an SSH shell.
def self.action_ssh
Builder.new.tap do |b|
b.use Builtin::ConfigValidate
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
if env[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
next
end
b2.use Builtin::Call, Builtin::IsState, :running do |env1, b3|
if !env1[:result]
b3.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_running")
next
end
b3.use Builtin::SSHExec
end
end
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use CheckCreated
# b.use CheckAccessible
b.use CheckRunning
b.use Vagrant::Action::Builtin::SSHExec
end
end
# This is the action that will run a single SSH command.
def self.action_ssh_run
Builder.new.tap do |b|
b.use Builtin::ConfigValidate
b.use Builtin::Call, Builtin::IsState, :not_created do |env, b2|
if env[:result]
b2.use Builtin::Message, I18n.t("vagrant_lxc.messages.not_created")
next
end
b2.use Builtin::Call, Builtin::IsState, :running do |env1, b3|
if !env1[:result]
raise Vagrant::Errors::VMNotRunningError
next
end
b3.use Builtin::SSHRun
end
end
Vagrant::Action::Builder.new.tap do |b|
# b.use CheckLXC
b.use CheckCreated
# b.use CheckAccessible
b.use CheckRunning
b.use Vagrant::Action::Builtin::SSHRun
end
end
end

View file

@ -0,0 +1,11 @@
module Vagrant
module LXC
module Action
class BaseAction
def initialize(app, env)
@app = app
end
end
end
end
end

View file

@ -1,45 +1,54 @@
module Vagrant
module LXC
module Action
class Boot
def initialize(app, env)
@app = app
end
class Boot < BaseAction
def call(env)
@env = env
driver = env[:machine].provider.driver
config = env[:machine].provider_config
utsname = env[:machine].config.vm.hostname || env[:machine].id
if driver.supports_new_config_format
config.customize 'uts.name', utsname
else
config.customize 'utsname', utsname
# Allows this middleware to be called multiple times. We need to
# support this as base boxes might have after create scripts which
# require SSH access
unless env[:machine].state.running?
env[:ui].info I18n.t("vagrant.actions.vm.boot.booting")
env[:machine].provider.container.start(config)
raise Vagrant::Errors::VMFailedToBoot if !wait_for_boot
end
# Fix apparmor issues when starting Ubuntu 14.04 containers
# See https://github.com/fgrehm/vagrant-lxc/issues/278 for more information
if Dir.exists?('/sys/fs/pstore')
config.customize 'mount.entry', '/sys/fs/pstore sys/fs/pstore none bind,optional 0 0'
end
# Make selinux read-only, see
# https://github.com/fgrehm/vagrant-lxc/issues/301
if Dir.exists?('/sys/fs/selinux')
config.customize 'mount.entry', '/sys/fs/selinux sys/fs/selinux none bind,ro 0 0'
end
if config.tmpfs_mount_size && !config.tmpfs_mount_size.empty?
# Make /tmp a tmpfs to prevent init scripts from nuking synced folders mounted in here
config.customize 'mount.entry', "tmpfs tmp tmpfs nodev,nosuid,size=#{config.tmpfs_mount_size} 0 0"
end
env[:ui].info I18n.t("vagrant_lxc.messages.starting")
driver.start(config.customizations)
@app.call env
end
# Stolen from on VagrantPlugins::ProviderVirtualBox::Action::Boot
def wait_for_boot
@env[:ui].info I18n.t("vagrant.actions.vm.boot.waiting")
@env[:machine].config.ssh.max_tries.to_i.times do |i|
if @env[:machine].communicate.ready?
@env[:ui].info I18n.t("vagrant.actions.vm.boot.ready")
return true
end
# Return true so that the vm_failed_to_boot error doesn't
# get shown
return true if @env[:interrupted]
# TODO: Find out if there is a command to check if the machine is
# starting, `lxc-monitor` shows this information, but I've
# never seen it on `lxc-info` which is what it is being used
# to determine container status
# If the VM is not starting or running, something went wrong
# and we need to show a useful error.
state = @env[:machine].provider.state.id
raise Vagrant::Errors::VMFailedToRun if state != :starting && state != :running
sleep 2 if !@env["vagrant.test"]
end
@env[:ui].error I18n.t("vagrant.actions.vm.boot.failed")
false
end
end
end
end

View file

@ -0,0 +1,17 @@
module Vagrant
module LXC
module Action
class CheckCreated < BaseAction
def call(env)
unless env[:machine].state.created?
raise Vagrant::Errors::VMNotCreatedError
end
# Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares)
@app.call(env)
end
end
end
end
end

View file

@ -0,0 +1,17 @@
module Vagrant
module LXC
module Action
class CheckRunning < BaseAction
def call(env)
unless env[:machine].state.running?
raise Vagrant::Errors::VMNotRunningError
end
# Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares)
@app.call(env)
end
end
end
end
end

View file

@ -1,56 +0,0 @@
module Vagrant
module LXC
module Action
class ClearForwardedPorts
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::clear_forwarded_ports")
end
def call(env)
@env = env
if redir_pids.any?
env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting")
redir_pids.each do |pid|
next unless is_redir_pid?(pid[0])
@logger.debug "Killing pid #{pid[0]}"
if pid[1]
system "sudo pkill -TERM -P #{pid[0]}"
else
system "pkill -TERM -P #{pid[0]}"
end
end
@logger.info "Removing redir pids files"
remove_redir_pids
else
@logger.info "No redir pids found"
end
@app.call env
end
protected
def redir_pids
@redir_pids = Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].map do |file|
port_number = File.basename(file).split(/[^\d]/).join
[ File.read(file).strip.chomp , Integer(port_number) <= 1024 ]
end
end
def is_redir_pid?(pid)
@logger.debug "Checking if #{pid} is a redir process with `ps -o cmd= #{pid}`"
`ps -o cmd= #{pid}`.strip.chomp =~ /redir/
end
def remove_redir_pids
Dir[@env[:machine].data_dir.join('pids').to_s + "/redir_*.pid"].each do |file|
File.delete file
end
end
end
end
end
end

View file

@ -1,30 +0,0 @@
require "fileutils"
module Vagrant
module LXC
module Action
class CompressRootFS
def initialize(app, env)
@app = app
end
def call(env)
raise Vagrant::Errors::VMPowerOffToPackage if env[:machine].provider.state.id != :stopped
env[:ui].info I18n.t("vagrant.actions.lxc.compressing_rootfs")
@rootfs = env['package.rootfs'] = env[:machine].provider.driver.compress_rootfs
@app.call env
recover # called to remove the rootfs tarball
end
def recover(*)
if @rootfs && File.exist?(@rootfs)
FileUtils.rm_rf(File.dirname @rootfs)
end
end
end
end
end
end

View file

@ -1,64 +1,13 @@
module Vagrant
module LXC
module Action
class Create
def initialize(app, env)
@app = app
end
class Create < BaseAction
def call(env)
config = env[:machine].provider_config
container_name = config.container_name
case container_name
when :machine
container_name = env[:machine].name.to_s
when String
# Nothing to do here, move along...
else
container_name = generate_container_name(env)
end
backingstore = config.backingstore
if backingstore.nil?
backingstore = config.privileged ? "best" : "dir"
end
driver = env[:machine].provider.driver
template_options = env[:lxc_template_opts]
if driver.supports_new_config_format
if env[:lxc_box_config]
driver.update_config_keys(env[:lxc_box_config])
end
else
template_options['--oldconfig'] = ''
end
driver.create(
container_name,
backingstore,
config.backingstore_options,
env[:lxc_template_src],
env[:lxc_template_config],
template_options
)
driver.update_config_keys
env[:machine].id = container_name
machine_id = env[:machine].provider.container.create(env[:machine].box.metadata)
env[:machine].id = machine_id
env[:just_created] = true
@app.call env
end
def generate_container_name(env)
container_name = "#{env[:root_path].basename}_#{env[:machine].name}"
container_name.gsub!(/[^-a-z0-9_]/i, "")
# milliseconds + random number suffix to allow for simultaneous
# `vagrant up` of the same box in different dirs
container_name << "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
# Trim container name to 64 chars, keeping "randomness"
trim_point = container_name.size > 64 ? -64 : -(container_name.size)
container_name[trim_point..-1]
end
end
end
end

View file

@ -0,0 +1,16 @@
module Vagrant
module LXC
module Action
class Created < BaseAction
def call(env)
# Set the result to be true if the machine is created.
env[:result] = env[:machine].state.created?
# Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares)
@app.call(env)
end
end
end
end
end

View file

@ -1,14 +1,10 @@
module Vagrant
module LXC
module Action
class Destroy
def initialize(app, env)
@app = app
end
class Destroy < BaseAction
def call(env)
env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying")
env[:machine].provider.driver.destroy
env[:machine].provider.container.destroy
env[:machine].id = nil
@app.call env
end

View file

@ -1,17 +0,0 @@
require "vagrant/action/builtin/confirm"
module Vagrant
module LXC
module Action
class DestroyConfirm < Vagrant::Action::Builtin::Confirm
def initialize(app, env)
force_key = :force_confirm_destroy
message = I18n.t("vagrant.commands.destroy.confirmation",
:name => env[:machine].name)
super(app, env, message, force_key)
end
end
end
end
end

View file

@ -1,44 +0,0 @@
module Vagrant
module LXC
module Action
class FetchIpWithLxcInfo
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::fetch_ip_with_lxc_info")
end
def call(env)
env[:machine_ip] ||= assigned_ip(env)
ensure
@app.call(env)
end
def assigned_ip(env)
config = env[:machine].provider_config
fetch_ip_tries = config.fetch_ip_tries
driver = env[:machine].provider.driver
ip = ''
return config.ssh_ip_addr if not config.ssh_ip_addr.nil?
retryable(:on => LXC::Errors::ExecuteError, :tries => fetch_ip_tries, :sleep => 3) do
unless ip = get_container_ip_from_ip_addr(driver)
# retry
raise LXC::Errors::ExecuteError, :command => "lxc-info"
end
end
ip
end
# From: https://github.com/lxc/lxc/blob/staging/src/python-lxc/lxc/__init__.py#L371-L385
def get_container_ip_from_ip_addr(driver)
output = driver.info '-iH'
if output =~ /^([0-9.]+)/
return $1.to_s
end
end
end
end
end
end

View file

@ -7,9 +7,11 @@ module Vagrant
end
def call(env)
if env[:machine].provider.state.id == :running
env[:ui].info I18n.t("vagrant_lxc.messages.force_shutdown")
env[:machine].provider.driver.forced_halt
if env[:machine].provider.state.running?
env[:ui].info I18n.t("vagrant.actions.vm.halt.force")
# TODO: Container#halt is kinda graceful as well, if it doesn't
# work we can issue a lxc-stop.
env[:machine].provider.container.halt
end
@app.call(env)

View file

@ -1,121 +0,0 @@
require 'open3'
module Vagrant
module LXC
module Action
class ForwardPorts
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::lxc::action::forward_ports")
end
def call(env)
@env = env
# Get the ports we're forwarding
env[:forwarded_ports] = compile_forwarded_ports(env[:machine].config)
if @env[:forwarded_ports].any? and not redir_installed?
raise Errors::RedirNotInstalled
end
# Warn if we're port forwarding to any privileged ports
env[:forwarded_ports].each do |fp|
if fp[:host] <= 1024
env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
break
end
end
# Continue, we need the VM to be booted in order to grab its IP
@app.call env
if @env[:forwarded_ports].any?
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
forward_ports
end
end
def forward_ports
@env[:forwarded_ports].each do |fp|
message_attributes = {
# TODO: Add support for multiple adapters
:adapter => 'eth0',
:guest_port => fp[:guest],
:host_port => fp[:host]
}
# TODO: Remove adapter from logging
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry",
message_attributes))
redir_pid = redirect_port(
fp[:host_ip],
fp[:host],
fp[:guest_ip] || @env[:machine].provider.ssh_info[:host],
fp[:guest]
)
store_redir_pid(fp[:host], redir_pid)
end
end
private
def compile_forwarded_ports(config)
mappings = {}
config.vm.networks.each do |type, options|
next if options[:disabled]
# TODO: Deprecate this behavior of "automagically" skipping ssh forwarded ports
if type == :forwarded_port && options[:id] != 'ssh'
if options.fetch(:host_ip, '').to_s.strip.empty?
options[:host_ip] = '127.0.0.1'
end
mappings[options[:host]] = options
end
end
mappings.values
end
def redirect_port(host_ip, host_port, guest_ip, guest_port)
if redir_version >= 3
params = %W( -n #{host_ip}:#{host_port} #{guest_ip}:#{guest_port} )
else
params = %W( --lport=#{host_port} --caddr=#{guest_ip} --cport=#{guest_port} )
params.unshift "--laddr=#{host_ip}" if host_ip
end
params << '--syslog' if ENV['REDIR_LOG']
if host_port < 1024
redir_cmd = "sudo redir #{params.join(' ')} 2>/dev/null"
else
redir_cmd = "redir #{params.join(' ')} 2>/dev/null"
end
@logger.debug "Forwarding port with `#{redir_cmd}`"
spawn redir_cmd
end
def store_redir_pid(host_port, redir_pid)
data_dir = @env[:machine].data_dir.join('pids')
data_dir.mkdir unless data_dir.directory?
data_dir.join("redir_#{host_port}.pid").open('w') do |pid_file|
pid_file.write(redir_pid)
end
end
def redir_version
stdout, stderr, _ = Open3.capture3 "redir --version"
# For some weird reason redir printed version information in STDERR prior to 3.2
version = stdout.empty? ? stderr : stdout
version.split('.')[0].to_i
end
def redir_installed?
system "which redir > /dev/null"
end
end
end
end
end

View file

@ -1,47 +0,0 @@
module Vagrant
module LXC
module Action
class GcPrivateNetworkBridges
def initialize(app, env)
@app = app
end
def call(env)
was_running = env[:machine].provider.state.id == :running
# Continue execution, we need the container to be stopped
@app.call(env)
was_running = was_running && env[:machine].provider.state.id != :running
if was_running && private_network_configured?(env[:machine].config)
private_network_configured?(env[:machine].config)
remove_bridges_that_are_not_in_use(env)
end
end
def private_network_configured?(config)
config.vm.networks.find do |type, _|
type.to_sym == :private_network
end
end
def remove_bridges_that_are_not_in_use(env)
env[:machine].config.vm.networks.find do |type, config|
next if type.to_sym != :private_network
bridge = config.fetch(:lxc__bridge_name)
driver = env[:machine].provider.driver
if ! driver.bridge_is_in_use?(bridge)
env[:ui].info I18n.t("vagrant_lxc.messages.remove_bridge", name: bridge)
unless ['lxcbr0', 'virbr0'].include? bridge
driver.remove_bridge(bridge)
end
end
end
end
end
end
end
end

View file

@ -2,93 +2,44 @@ module Vagrant
module LXC
module Action
# Prepare arguments to be used for lxc-create
class HandleBoxMetadata
SUPPORTED_VERSIONS = ['1.0.0', '2', '3']
class HandleBoxMetadata < BaseAction
LXC_TEMPLATES_PATH = Pathname.new("/usr/share/lxc/templates")
TEMP_PREFIX = "vagrant-lxc-rootfs-temp-"
def initialize(app, env)
@app = app
super
@logger = Log4r::Logger.new("vagrant::lxc::action::handle_box_metadata")
end
def call(env)
@env = env
@box = @env[:machine].box
env[:ui].info I18n.t("vagrant.actions.vm.import.importing",
:name => env[:machine].box.name)
@env[:ui].info I18n.t("vagrant.actions.vm.import.importing",
:name => @env[:machine].box.name)
rootfs_cache = Dir.mktmpdir(TEMP_PREFIX)
box = env[:machine].box
template_name = "vagrant-#{box.name}"
@logger.info 'Validating box contents'
validate_box
@logger.info 'Setting box options on environment'
@env[:lxc_template_src] = template_src
@env[:lxc_template_opts] = template_opts
# FIXME: Remove support for pre 1.0.0 boxes
if box_version != '1.0.0'
@env[:ui].warn "WARNING: You are using a base box that has a format that has been deprecated, please upgrade to a new one."
@env[:lxc_template_opts].merge!(
'--auth-key' => Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s
)
# Prepends "lxc-" to the template file so that `lxc-create` is able to find it
lxc_template_src = box.directory.join('lxc-template').to_s
unless File.exists?(lxc_template_src)
raise Errors::TemplateFileMissing.new name: box.name
end
dest = LXC_TEMPLATES_PATH.join("lxc-#{template_name}").to_s
@logger.debug('Copying LXC template into place')
system(%Q[sudo su root -c "cp #{lxc_template_src} #{dest}"])
if template_config_file.exist?
@env[:lxc_box_config] = template_config_file.to_s
@env[:lxc_template_opts].merge!('--config' => template_config_file.to_s)
elsif old_template_config_file.exist?
@env[:lxc_box_config] = old_template_config_file.to_s
@env[:lxc_template_config] = old_template_config_file.to_s
end
@logger.debug('Extracting rootfs')
system(%Q[sudo su root -c "cd #{box.directory} && tar xfz rootfs.tar.gz -C #{rootfs_cache}"])
@app.call env
end
def template_src
@template_src ||=
if (box_template = @box.directory.join('lxc-template')).exist?
box_template.to_s
else
Vagrant::LXC.source_root.join('scripts/lxc-template').to_s
end
end
def template_config_file
@template_config_file ||= @box.directory.join('lxc-config')
end
# TODO: Remove this once we remove compatibility for < 1.0.0 boxes
def old_template_config_file
@old_template_config_file ||= @box.directory.join('lxc.conf')
end
def template_opts
@template_opts ||= @box.metadata.fetch('template-opts', {}).dup.merge!(
'--tarball' => rootfs_tarball
box.metadata.merge!(
'template-name' => template_name,
'rootfs-cache-path' => rootfs_cache
)
end
def rootfs_tarball
@rootfs_tarball ||= @box.directory.join('rootfs.tar.gz').to_s
end
@app.call(env)
def validate_box
unless SUPPORTED_VERSIONS.include? box_version
raise Errors::IncompatibleBox.new name: @box.name,
found: box_version,
supported: SUPPORTED_VERSIONS.join(', ')
end
unless File.exists?(template_src)
raise Errors::TemplateFileMissing.new name: @box.name
end
unless File.exists?(rootfs_tarball)
raise Errors::RootFSTarballMissing.new name: @box.name
end
end
def box_version
@box.metadata.fetch('version')
ensure
system %Q[sudo su root -c "rm -rf #{rootfs_cache}"]
end
end
end

View file

@ -0,0 +1,15 @@
module Vagrant
module LXC
module Action
class IsRunning < BaseAction
def call(env)
env[:result] = env[:machine].state.running?
# Call the next if we have one (but we shouldn't, since this
# middleware is built to run with the Call-type middlewares)
@app.call(env)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Vagrant
module LXC
module Action
class Network < BaseAction
def call(env)
# TODO: Validate network configuration prior to anything below
@env = env
env[:machine].config.vm.networks.each do |type, options|
# We only handle private networks
next if type != :private_network
env[:machine].provider_config.start_opts << "lxc.network.ipv4=#{options[:ip]}/24"
end
# Continue the middleware chain.
@app.call(env)
end
end
end
end
end

View file

@ -1,64 +0,0 @@
module Vagrant
module LXC
module Action
class PrepareNFSSettings
include Vagrant::Util::Retryable
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
end
def call(env)
@machine = env[:machine]
@app.call(env)
# if using_nfs? # TODO: && !privileged_container?
# raise Errors::NfsWithoutPrivilegedError
# end
if using_nfs?
@logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
add_ips_to_env!(env)
end
end
# We're using NFS if we have any synced folder with NFS configured. If
# we are not using NFS we don't need to do the extra work to
# populate these fields in the environment.
def using_nfs?
@machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
end
# TODO:
# def privileged_container?
# @machine.provider.driver.privileged?(@machine.id)
# end
# Extracts the proper host and guest IPs for NFS mounts and stores them
# in the environment for the SyncedFolder action to use them in
# mounting.
#
# The ! indicates that this method modifies its argument.
def add_ips_to_env!(env)
provider = @machine.provider
host_ip = read_host_ip
machine_ip = provider.ssh_info[:host]
raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip
env[:nfs_host_ip] = host_ip
env[:nfs_machine_ip] = machine_ip
end
def read_host_ip
@machine.communicate.execute 'echo $SSH_CLIENT' do |buffer, output|
return output.chomp.split(' ')[0] if buffer == :stdout
end
end
end
end
end
end

View file

@ -1,19 +0,0 @@
module Vagrant
module LXC
module Action
class PrepareNFSValidIds
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::vm::nfs")
end
def call(env)
machine = env[:machine]
env[:nfs_valid_ids] = machine.provider.driver.all_containers
@app.call(env)
end
end
end
end
end

View file

@ -1,46 +0,0 @@
module Vagrant
module LXC
module Action
class PrivateNetworks
def initialize(app, env)
@app = app
end
def call(env)
@app.call(env)
if private_network_configured?(env[:machine].config)
env[:ui].output(I18n.t("vagrant_lxc.messages.setup_private_network"))
configure_private_networks(env)
end
end
def private_network_configured?(config)
config.vm.networks.find do |type, _|
type.to_sym == :private_network
end
end
def configure_private_networks(env)
env[:machine].config.vm.networks.find do |type, config|
next if type.to_sym != :private_network
container_name = env[:machine].provider.driver.container_name
address_type = config[:type]
ip = config[:ip]
bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) }
bridge = config.fetch(:lxc__bridge_name)
env[:machine].provider.driver.configure_private_network(bridge, bridge_ip, container_name, address_type, ip)
end
end
def build_bridge_ip(ip)
if ip
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
end
end
end
end
end
end

View file

@ -1,60 +0,0 @@
require 'fileutils'
module Vagrant
module LXC
module Action
class SetupPackageFiles
def initialize(app, env)
@app = app
env["package.include"] ||= []
env["package.vagrantfile"] ||= nil
end
def call(env)
@env = env
create_package_temp_dir
move_rootfs_to_pkg_dir
copy_box_files_to_pkg_dir
@app.call env
recover # called to cleanup temp directory
end
def recover(*)
if @temp_dir && File.exist?(@temp_dir)
FileUtils.rm_rf(@temp_dir)
end
end
private
def create_package_temp_dir
@env[:ui].info I18n.t("vagrant.actions.vm.export.create_dir")
@temp_dir = @env["package.directory"] = @env[:tmp_path].join("container-export-#{Time.now.to_i.to_s}")
FileUtils.mkpath(@temp_dir)
end
def move_rootfs_to_pkg_dir
FileUtils.mv @env['package.rootfs'].to_s, @env['package.directory'].to_s
end
def copy_box_files_to_pkg_dir
box_dir = @env[:machine].box.directory
FileUtils.cp box_dir.join('metadata.json').to_s, @env['package.directory'].to_s
if (template = box_dir.join('lxc-template')).exist?
FileUtils.cp template.to_s, @env['package.directory'].to_s
end
if (conf = box_dir.join('lxc.conf')).exist?
FileUtils.cp conf.to_s, @env['package.directory'].to_s
end
if (conf = box_dir.join('lxc-config')).exist?
FileUtils.cp conf.to_s, @env['package.directory'].to_s
end
end
end
end
end
end

View file

@ -0,0 +1,65 @@
module Vagrant
module LXC
module Action
class ShareFolders < BaseAction
def call(env)
@env = env
prepare_folders
add_start_opts
@app.call env
end
# This method returns an actual list of VirtualBox shared
# folders to create and their proper path.
def shared_folders
{}.tap do |result|
@env[:machine].config.vm.synced_folders.each do |id, data|
# Ignore NFS shared folders
next if data[:nfs]
# This to prevent overwriting the actual shared folders data
result[id] = data.dup
end
end
end
# Prepares the shared folders by verifying they exist and creating them
# if they don't.
def prepare_folders
shared_folders.each do |id, options|
hostpath = Pathname.new(options[:hostpath]).expand_path(@env[:root_path])
if !hostpath.directory? && options[:create]
# Host path doesn't exist, so let's create it.
@logger.debug("Host path doesn't exist, creating: #{hostpath}")
begin
hostpath.mkpath
rescue Errno::EACCES
raise Vagrant::Errors::SharedFolderCreateFailed,
:path => hostpath.to_s
end
end
end
end
def add_start_opts
@env[:ui].info I18n.t("vagrant.actions.lxc.share_folders.preparing")
folders = []
shared_folders.each do |id, data|
folders << {
:name => id,
:hostpath => File.expand_path(data[:hostpath], @env[:root_path]),
:guestpath => data[:guestpath]
}
@env[:ui].info(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
:guest_path => data[:guestpath]))
end
config = @env[:machine].provider_config
@env[:machine].provider.container.share_folders(folders, config)
end
end
end
end
end

View file

@ -1,25 +0,0 @@
module Vagrant
module LXC
module Action
class WarnNetworks
def initialize(app, env)
@app = app
end
def call(env)
if public_network_configured?(env[:machine].config)
env[:ui].warn(I18n.t("vagrant_lxc.messages.warn_networks"))
end
@app.call(env)
end
def public_network_configured?(config)
config.vm.networks.find do |type, _|
type.to_sym == :public_network
end
end
end
end
end
end

View file

@ -1,58 +0,0 @@
module Vagrant
module LXC
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
'vagrant-lxc specific commands'
end
def initialize(argv, env)
@args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new.tap do |registry|
registry.register(:sudoers) do
require_relative 'sudoers'
Sudoers
end
end
super(argv, env)
end
def execute
# Print the help
return help if @args.include?("-h") || @args.include?("--help")
klazz = @subcommands.get(@sub_command.to_sym) if @sub_command
return help unless klazz
@logger.debug("Executing command: #{klazz} #{@sub_args.inspect}")
# Initialize and execute the command class
klazz.new(@sub_args, @env).execute
end
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant lxc <subcommand> [<args>]"
opts.separator ""
opts.separator "Available subcommands:"
# REFACTOR Use @subcommands.keys.sort
# https://github.com/mitchellh/vagrant/commit/4194da19c60956f6e59239c0145f772be257e79d
keys = []
@subcommands.each { |key, value| keys << key }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant lxc <subcommand> -h`"
end
@env.ui.info(opts.help, :prefix => false)
end
end
end
end
end

View file

@ -1,97 +0,0 @@
require 'tempfile'
require "vagrant-lxc/driver"
require "vagrant-lxc/sudo_wrapper"
module Vagrant
module LXC
module Command
class Sudoers < Vagrant.plugin("2", :command)
def initialize(argv, env)
super
@argv
@env = env
end
def execute
options = { user: ENV['USER'] }
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant lxc sudoers"
opts.separator ""
opts.on('-u user', '--user user', String, "The user for which to create the policy (defaults to '#{options[:user]}')") do |u|
options[:user] = u
end
end
argv = parse_options(opts)
return unless argv
wrapper_path = SudoWrapper.dest_path
wrapper = create_wrapper!
sudoers = create_sudoers!(options[:user], wrapper_path)
su_copy([
{source: wrapper, target: wrapper_path, mode: "0555"},
{source: sudoers, target: sudoers_path, mode: "0440"}
])
end
def sudoers_path
"/etc/sudoers.d/vagrant-lxc"
end
private
# This requires vagrant 1.5.2+ https://github.com/mitchellh/vagrant/commit/3371c3716278071680af9b526ba19235c79c64cb
def create_wrapper!
lxc_base_path = Driver.new("").containers_path
wrapper = Tempfile.new('lxc-wrapper').tap do |file|
template = Vagrant::Util::TemplateRenderer.new(
'sudoers.rb',
:template_root => Vagrant::LXC.source_root.join('templates').to_s,
:cmd_paths => build_cmd_paths_hash,
:lxc_base_path => lxc_base_path,
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/(?:\\d+?\\.\\d+?\\.\\d+?/)?gems/vagrant-lxc.+/scripts/pipework"
)
file.puts template.render
end
wrapper.close
wrapper.path
end
def create_sudoers!(user, command)
sudoers = Tempfile.new('vagrant-lxc-sudoers').tap do |file|
file.puts "# Automatically created by vagrant-lxc"
file.puts "#{user} ALL=(root) NOPASSWD: #{command}"
end
sudoers.close
sudoers.path
end
def su_copy(files)
commands = files.map { |file|
[
"rm -f #{file[:target]}",
"cp #{file[:source]} #{file[:target]}",
"chown root:root #{file[:target]}",
"chmod #{file[:mode]} #{file[:target]}"
]
}.flatten
system "echo \"#{commands.join("; ")}\" | sudo sh"
end
def build_cmd_paths_hash
{}.tap do |hash|
%w( which cat mkdir cp chown chmod rm tar chown ip ifconfig brctl ).each do |cmd|
hash[cmd] = `sudo which #{cmd}`.strip
end
hash['lxc_bin'] = Pathname(`sudo which lxc-create`.strip).parent.to_s
hash['ruby'] = Gem.ruby
end
end
end
end
end
end

View file

@ -1,76 +1,13 @@
module Vagrant
module LXC
class Config < Vagrant.plugin("2", :config)
# An array of container's configuration overrides to be provided to `lxc-start`.
# An array of options to be passed to lxc-start when booting the machine.
#
# @return [Array]
attr_reader :customizations
# A string that contains the backing store type used with lxc-create -B
attr_accessor :backingstore
# Optional arguments for the backing store, such as --fssize, --fstype, ...
#
# @return [Array]
attr_accessor :backingstore_options
# A string to explicitly set the container name. To use the vagrant
# machine name, set this to :machine
attr_accessor :container_name
# Size (as a string like '400M') of the tmpfs to mount at /tmp on boot.
# Set to false or nil to disable the tmpfs mount altogether. Defaults to '2G'.
attr_accessor :tmpfs_mount_size
attr_accessor :fetch_ip_tries
attr_accessor :ssh_ip_addr
# Whether the container needs to be privileged. Defaults to true (unprivileged containers
# is a very new feature in vagrant-lxc). If false, will try creating an unprivileged
# container. If it can't, will revert to the old "sudo wrapper" method to create a privileged
# container.
attr_accessor :privileged
attr_reader :start_opts
def initialize
@customizations = []
@backingstore = UNSET_VALUE
@backingstore_options = []
@container_name = UNSET_VALUE
@tmpfs_mount_size = UNSET_VALUE
@fetch_ip_tries = UNSET_VALUE
@ssh_ip_addr = UNSET_VALUE
@privileged = UNSET_VALUE
end
# Customize the container by calling `lxc-start` with the given
# configuration overrides.
#
# For example, if you want to set the memory limit, you can use it
# like: config.customize 'cgroup.memory.limit_in_bytes', '400M'
#
# When `lxc-start`ing the container, vagrant-lxc will pass in
# "-s lxc.cgroup.memory.limit_in_bytes=400M" to it.
#
# @param [String] key Configuration key to override
# @param [String] value Configuration value to override
def customize(key, value)
@customizations << [key, value]
end
# Stores options for backingstores like lvm, btrfs, etc
def backingstore_option(key, value)
@backingstore_options << [key, value]
end
def finalize!
@container_name = nil if @container_name == UNSET_VALUE
@backingstore = nil if @backingstore == UNSET_VALUE
@existing_container_name = nil if @existing_container_name == UNSET_VALUE
@tmpfs_mount_size = '2G' if @tmpfs_mount_size == UNSET_VALUE
@fetch_ip_tries = 10 if @fetch_ip_tries == UNSET_VALUE
@ssh_ip_addr = nil if @ssh_ip_addr == UNSET_VALUE
@privileged = true if @privileged == UNSET_VALUE
@start_opts = []
end
end
end

View file

@ -0,0 +1,170 @@
# FIXME: Ruby 1.8 users dont have SecureRandom
require 'securerandom'
require "vagrant-lxc/errors"
require "vagrant-lxc/container/cli"
require "vagrant/util/retryable"
require "vagrant/util/subprocess"
module Vagrant
module LXC
class Container
# Root folder where containers are stored
CONTAINERS_PATH = '/var/lib/lxc'
# Default LXC configs
LXC_DEFAULTS_PATH = '/etc/default/lxc'
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
# This is raised if the container can't be found when initializing it with
# a name.
class NotFound < StandardError; end
attr_reader :name
def initialize(name, cli = CLI.new(name))
@name = name
@cli = cli
@logger = Log4r::Logger.new("vagrant::provider::lxc::container")
end
def validate!
raise NotFound if @name && ! @cli.list.include?(@name)
end
def base_path
Pathname.new("#{CONTAINERS_PATH}/#{@name}")
end
def rootfs_path
Pathname.new("#{base_path}/rootfs")
end
def create(metadata = {})
@logger.debug('Creating container using lxc-create...')
@name = SecureRandom.hex(6)
public_key = Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s
meta_opts = metadata.fetch('template-opts', {}).merge(
'--auth-key' => public_key,
'--cache' => metadata.fetch('rootfs-cache-path')
)
@cli.name = @name
@cli.create(metadata.fetch('template-name'), meta_opts)
@name
end
def share_folders(folders, config)
folders.each do |folder|
guestpath = rootfs_path.join(folder[:guestpath].gsub(/^\//, ''))
unless guestpath.directory?
begin
system "sudo mkdir -p #{guestpath.to_s}"
rescue Errno::EACCES
raise Vagrant::Errors::SharedFolderCreateFailed,
:path => guestpath.to_s
end
end
config.start_opts << "lxc.mount.entry=#{folder[:hostpath]} #{guestpath} none bind 0 0"
end
end
def start(config)
@logger.info('Starting container...')
opts = config.start_opts.dup
if ENV['LXC_START_LOG_FILE']
extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
end
@cli.transition_to(:running) { |c| c.start(opts, (extra || nil)) }
end
def halt
@logger.info('Shutting down container...')
# TODO: issue an lxc-stop if a timeout gets reached
@cli.transition_to(:stopped) { |c| c.shutdown }
end
def destroy
@cli.destroy
end
def state
if @name
@cli.state
end
end
def assigned_ip
unless File.read(base_path.join('config')) =~ /^lxc\.network\.hwaddr\s*=\s*([a-z0-9:]+)\s*$/
raise 'Unknown Container MAC Address'
end
mac_addr = $1
ip = ''
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
# See: http://programminglinuxblog.blogspot.com.br/2007/11/detecting-ip-address-from-mac-address.html
unless ip = get_container_ip_from_arp(mac_addr)
# Ping subnet and try to get ip again
ping_subnet!
end
end
ip
end
def get_container_ip_from_arp(mac_addr)
r = raw 'arp', '-n'
# If the command was a failure then raise an exception that is nicely
# handled by Vagrant.
if r.exit_code != 0
if @interrupted
@logger.info("Exit code != 0, but interrupted. Ignoring.")
else
raise LXC::Errors::ExecuteError, :command => ['arp', '-n'].inspect
end
end
if r.stdout.gsub("\r\n", "\n").strip =~ /^([0-9.]+).+#{Regexp.escape mac_addr}/
return $1.to_s
end
end
# FIXME: Should output an error friendly message in case fping is not installed
def ping_subnet!
raise LXC::Errors::UnknownLxcConfigFile unless File.exists?(LXC_DEFAULTS_PATH)
raise LXC::Errors::UnknownLxcBridgeAddress unless
File.read(LXC_DEFAULTS_PATH) =~ /^LXC_ADDR\="?([0-9.]+)"?.*$/
cmd = ['fping', '-c', '1', '-g', '-q', "#{$1}/24"]
raw(*cmd)
raise LXC::Errors::ExecuteError, :command => cmd.inspect
end
# TODO: Review code below this line, it was pretty much a copy and paste from VirtualBox base driver
def raw(*command, &block)
int_callback = lambda do
@interrupted = true
@logger.info("Interrupted.")
end
# Append in the options for subprocess
command << { :notify => [:stdout, :stderr] }
Vagrant::Util::Busy.busy(int_callback) do
Vagrant::Util::Subprocess.execute(*command, &block)
end
end
end
end
end

View file

@ -0,0 +1,124 @@
require "vagrant/util/retryable"
require "vagrant/util/subprocess"
require "vagrant-lxc/errors"
module Vagrant
module LXC
class Container
class CLI
attr_accessor :name
class TransitionBlockNotProvided < RuntimeError; end
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
def initialize(name = nil)
@name = name
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
end
def list
run(:ls).split(/\s+/).uniq
end
def state
if @name && run(:info, '--name', @name) =~ /^state:[^A-Z]+([A-Z]+)$/
$1.downcase.to_sym
elsif @name
:unknown
end
end
def create(template, template_opts = {})
extra = template_opts.to_a.flatten
extra.unshift '--' unless extra.empty?
run :create,
# lxc-create options
'--template', template,
'--name', @name,
*extra
end
def destroy
run :destroy, '--name', @name
end
def start(configs = [], extra_opts = [])
configs = configs.map { |conf| ["-s", conf] }.flatten
configs += extra_opts if extra_opts
run :start, '-d', '--name', @name, *configs
end
def shutdown
run :shutdown, '--name', @name
end
def transition_to(state, &block)
raise TransitionBlockNotProvided unless block_given?
yield self
run :wait, '--name', @name, '--state', state.to_s.upcase
end
private
def run(command, *args)
execute('sudo', "lxc-#{command}", *args)
end
# TODO: Review code below this line, it was pretty much a copy and
# paste from VirtualBox base driver and has no tests
def execute(*command, &block)
# Get the options hash if it exists
opts = {}
opts = command.pop if command.last.is_a?(Hash)
tries = 0
tries = 3 if opts[:retryable]
sleep = opts.fetch(:sleep, 1)
# Variable to store our execution result
r = nil
retryable(:on => LXC::Errors::ExecuteError, :tries => tries, :sleep => sleep) do
# Execute the command
r = raw(*command, &block)
# If the command was a failure, then raise an exception that is
# nicely handled by Vagrant.
if r.exit_code != 0
if @interrupted
@logger.info("Exit code != 0, but interrupted. Ignoring.")
else
raise LXC::Errors::ExecuteError, :command => command.inspect
end
end
end
# Return the output, making sure to replace any Windows-style
# newlines with Unix-style.
r.stdout.gsub("\r\n", "\n")
end
def raw(*command, &block)
int_callback = lambda do
@interrupted = true
@logger.info("Interrupted.")
end
# Append in the options for subprocess
command << { :notify => [:stdout, :stderr] }
Vagrant::Util::Busy.busy(int_callback) do
Vagrant::Util::Subprocess.execute(*command, &block)
end
end
end
end
end
end

View file

@ -1,290 +0,0 @@
require "vagrant/util/retryable"
require "vagrant/util/subprocess"
require "vagrant-lxc/errors"
require "vagrant-lxc/driver/cli"
require "vagrant-lxc/sudo_wrapper"
require "etc"
require "tempfile"
module Vagrant
module LXC
class Driver
# This is raised if the container can't be found when initializing it with
# a name.
class ContainerNotFound < StandardError; end
# Default root folder where container configs are stored
attr_reader :container_name,
:customizations
def initialize(container_name, sudo_wrapper = nil, cli = nil, privileged: true)
@container_name = container_name
@sudo_wrapper = sudo_wrapper || SudoWrapper.new(privileged: privileged)
@cli = cli || CLI.new(@sudo_wrapper, container_name)
@logger = Log4r::Logger.new("vagrant::provider::lxc::driver")
@customizations = []
end
def validate!
raise ContainerNotFound if @container_name && ! @cli.list.include?(@container_name)
end
# Root folder where container configs are stored
def containers_path
@containers_path ||= @cli.config('lxc.lxcpath')
end
def all_containers
@cli.list
end
def base_path
Pathname.new("#{containers_path}/#{@container_name}")
end
def config_path
base_path.join('config').to_s
end
def rootfs_path
pathtype, path = config_string.match(/^lxc\.rootfs(?:\.path)?\s+=\s+(.+:)?(.+)$/)[1..2]
case pathtype
when 'overlayfs:'
# Split on colon (:), ignoring any colon escaped by an escape character ( \ )
# Pays attention to when the escape character is itself escaped.
_, overlay_path = config_entry.split(/(?<!\\)(?:\\\\)*:/)
if overlay_path
Pathname.new(overlay_path)
else
# Malformed: fall back to prior behaviour
Pathname.new(path)
end
else
Pathname.new(path)
end
end
def mac_address
return @mac_address if @mac_address
if config_string =~ /^lxc\.network\.hwaddr\s*+=\s*+(.+)$/
@mac_address = $1
end
end
def config_string
@sudo_wrapper.run('cat', config_path)
end
def create(name, backingstore, backingstore_options, template_path, config_file, template_options = {})
@cli.name = @container_name = name
@logger.debug "Creating container..."
@cli.create template_path, backingstore, backingstore_options, config_file, template_options
end
def share_folders(folders)
folders.each do |f|
share_folder(f[:hostpath], f[:guestpath], f.fetch(:mount_options, nil))
end
end
def share_folder(host_path, guest_path, mount_options = nil)
guest_path = guest_path.gsub(/^\//, '').gsub(' ', '\\\040')
mount_options = Array(mount_options || ['bind', 'create=dir'])
host_path = host_path.to_s.gsub(' ', '\\\040')
@customizations << ['mount.entry', "#{host_path} #{guest_path} none #{mount_options.join(',')} 0 0"]
end
def start(customizations)
@logger.info('Starting container...')
if ENV['LXC_START_LOG_FILE']
extra = ['-o', ENV['LXC_START_LOG_FILE'], '-l', 'DEBUG']
end
prune_customizations
write_customizations(customizations + @customizations)
@cli.start(extra)
end
def forced_halt
@logger.info('Shutting down container...')
@cli.transition_to(:stopped) { |c| c.stop }
end
def destroy
@cli.destroy
end
def attach(*command)
@cli.attach(*command)
end
def info(*command)
@cli.info(*command)
end
def configure_private_network(bridge_name, bridge_ip, container_name, address_type, ip)
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
if ip
ip += '/24'
end
if ! bridge_exists?(bridge_name)
if not bridge_ip
raise "Bridge is missing and no IP was specified!"
end
@logger.info "Creating the bridge #{bridge_name}"
cmd = [
'brctl',
'addbr',
bridge_name
]
@sudo_wrapper.run(*cmd)
end
if ! bridge_has_an_ip?(bridge_name)
if not bridge_ip
raise "Bridge has no IP and none was specified!"
end
@logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
cmd = [
'ip',
'addr',
'add',
"#{bridge_ip}/24",
'dev',
bridge_name
]
@sudo_wrapper.run(*cmd)
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'up')
end
cmd = [
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
bridge_name,
container_name,
ip ||= "dhcp"
]
@sudo_wrapper.run(*cmd)
end
def bridge_has_an_ip?(bridge_name)
@logger.info "Checking whether the bridge #{bridge_name} has an IP"
`ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
end
def bridge_exists?(bridge_name)
@logger.info "Checking whether bridge #{bridge_name} exists"
brctl_output = `ip link | egrep -q " #{bridge_name}:"`
$?.to_i == 0
end
def bridge_is_in_use?(bridge_name)
# REFACTOR: This method is **VERY** hacky
@logger.info "Checking if bridge #{bridge_name} is in use"
brctl_output = `brctl show #{bridge_name} 2>/dev/null | tail -n +2 | grep -q veth`
$?.to_i == 0
end
def remove_bridge(bridge_name)
if ['lxcbr0', 'virbr0'].include? bridge_name
@logger.info "Skipping removal of system bridge #{bridge_name}"
return
end
return unless bridge_exists?(bridge_name)
@logger.info "Removing bridge #{bridge_name}"
@sudo_wrapper.run('ip', 'link', 'set', bridge_name, 'down')
@sudo_wrapper.run('brctl', 'delbr', bridge_name)
end
def version
@version ||= @cli.version
end
def supports_new_config_format
Gem::Version.new(version) >= Gem::Version.new('2.1.0')
end
# TODO: This needs to be reviewed and specs needs to be written
def compress_rootfs
# TODO: Pass in tmpdir so we can clean up from outside
target_path = "#{Dir.mktmpdir}/rootfs.tar.gz"
@logger.info "Compressing '#{rootfs_path}' rootfs to #{target_path}"
@sudo_wrapper.run('tar', '--numeric-owner', '-cvzf', target_path, '-C',
rootfs_path.parent.to_s, "./#{rootfs_path.basename.to_s}")
@logger.info "Changing rootfs tarball owner"
user_details = Etc.getpwnam(Etc.getlogin)
@sudo_wrapper.run('chown', "#{user_details.uid}:#{user_details.gid}", target_path)
target_path
end
def state
if @container_name
@cli.state
end
end
def prune_customizations
# Use sed to just strip out the block of code which was inserted by Vagrant
@logger.debug 'Prunning vagrant-lxc customizations'
contents = config_string
contents.gsub! /^# VAGRANT-BEGIN(.|\s)*# VAGRANT-END\n/, ''
write_config(contents)
end
def update_config_keys(path = nil)
path = path || config_path
@cli.update_config(path)
rescue Errors::ExecuteError
# not on LXC 2.1+. Doesn't matter, ignore.
end
protected
def write_customizations(customizations)
customizations = customizations.map do |key, value|
"lxc.#{key}=#{value}"
end
customizations.unshift '# VAGRANT-BEGIN'
customizations << "# VAGRANT-END\n"
contents = config_string
contents << customizations.join("\n")
write_config(contents)
end
def write_config(contents)
confpath = base_path.join('config').to_s
begin
File.open(confpath, File::RDWR) do |file|
file.write contents
end
rescue
# We don't have permissions to write in the conf file. That's probably because it's a
# privileged container. Work around that through sudo_wrapper.
Tempfile.new('lxc-config').tap do |file|
file.chmod 0644
file.write contents
file.close
@sudo_wrapper.run 'cp', '-f', file.path, confpath
@sudo_wrapper.run 'chown', 'root:root', confpath
end
end
end
end
end
end

View file

@ -1,152 +0,0 @@
require "vagrant/util/retryable"
require "vagrant/util/subprocess"
require "vagrant-lxc/errors"
module Vagrant
module LXC
class Driver
class CLI
attr_accessor :name
class TransitionBlockNotProvided < RuntimeError; end
class TargetStateNotReached < RuntimeError
def initialize(target_state, state)
msg = "Target state '#{target_state}' not reached, currently on '#{state}'"
super(msg)
end
end
def initialize(sudo_wrapper, name = nil)
@sudo_wrapper = sudo_wrapper
@name = name
@logger = Log4r::Logger.new("vagrant::provider::lxc::container::cli")
end
def list
run(:ls).split(/\s+/).uniq
end
def version
return @version if @version
@version = run(:create, '--version')
if @version =~ /(lxc version:\s+|)(.+)\s*$/
@version = $2.downcase
else
# TODO: Raise an user friendly error
raise 'Unable to parse lxc version!'
end
end
def config(param)
run(:config, param).gsub("\n", '')
end
def update_config(path)
run('update-config', '-c', path)
end
def state
if @name && run(:info, '--name', @name, retryable: true) =~ /^state:[^A-Z]+([A-Z]+)$/i
$1.downcase.to_sym
elsif @name
:unknown
end
end
def create(template, backingstore, backingstore_options, config_file, template_opts = {})
if config_file
config_opts = ['-f', config_file]
end
extra = template_opts.to_a.flatten.reject { |elem| elem.empty? }
extra.unshift '--' unless extra.empty?
run :create,
'-B', backingstore,
'--template', template,
'--name', @name,
*(backingstore_options.to_a.flatten),
*(config_opts),
*extra
rescue Errors::ExecuteError => e
if e.stderr =~ /already exists/i
raise Errors::ContainerAlreadyExists, name: @name
else
raise
end
end
def destroy
run :destroy, '--name', @name
end
def start(options = [])
run :start, '-d', '--name', @name, *Array(options)
end
## lxc-stop will exit 2 if machine was already stopped
# Man Page:
# 2 The specified container exists but was not running.
def stop
begin
run :stop, '--name', @name
rescue LXC::Errors::ExecuteError => e
if e.exitcode == 2
@logger.debug "Machine already stopped, lxc-stop returned 2"
else
raise e
end
end
end
def attach(*cmd)
cmd = ['--'] + cmd
if cmd.last.is_a?(Hash)
opts = cmd.pop
namespaces = Array(opts[:namespaces]).map(&:upcase).join('|')
# HACK: The wrapper script should be able to handle this
if @sudo_wrapper.wrapper_path
namespaces = "'#{namespaces}'"
end
if namespaces
extra = ['--namespaces', namespaces]
end
end
run :attach, '--name', @name, *((extra || []) + cmd)
end
def info(*cmd)
run(:info, '--name', @name, *cmd)
end
def transition_to(target_state, tries = 30, timeout = 1, &block)
raise TransitionBlockNotProvided unless block_given?
yield self
while (last_state = self.state) != target_state && tries > 0
@logger.debug "Target state '#{target_state}' not reached, currently on '#{last_state}'"
sleep timeout
tries -= 1
end
unless last_state == target_state
# TODO: Raise an user friendly message
raise TargetStateNotReached.new target_state, last_state
end
end
private
def run(command, *args)
@sudo_wrapper.run("lxc-#{command}", *args)
end
end
end
end
end

View file

@ -1,62 +1,12 @@
require 'vagrant/errors'
module Vagrant
module LXC
module Errors
class ExecuteError < Vagrant::Errors::VagrantError
error_key(:lxc_execute_error)
attr_reader :stderr, :stdout, :exitcode
def initialize(message, *args)
super
if message.is_a?(Hash)
@stderr = message[:stderr]
@stdout = message[:stdout]
@exitcode = message[:exitcode]
end
end
end
# Raised when user interrupts a subprocess
class SubprocessInterruptError < Vagrant::Errors::VagrantError
error_key(:lxc_interrupt_error)
def initialize(message, *args)
super
end
end
class LxcLinuxRequired < Vagrant::Errors::VagrantError
error_key(:lxc_linux_required)
end
class LxcNotInstalled < Vagrant::Errors::VagrantError
error_key(:lxc_not_installed)
end
class ContainerAlreadyExists < Vagrant::Errors::VagrantError
error_key(:lxc_container_already_exists)
end
class CommandNotSupported < Vagrant::Errors::VagrantError
error_key(:lxc_command_not_supported)
end
# Box related errors
class TemplateFileMissing < Vagrant::Errors::VagrantError
error_key(:lxc_template_file_missing)
end
class TemplatesDirMissing < Vagrant::Errors::VagrantError
error_key(:lxc_templates_dir_missing)
end
class RootFSTarballMissing < Vagrant::Errors::VagrantError
error_key(:lxc_invalid_box_version)
end
class IncompatibleBox < Vagrant::Errors::VagrantError
error_key(:lxc_incompatible_box)
end
class RedirNotInstalled < Vagrant::Errors::VagrantError
error_key(:lxc_redir_not_installed)
end
end
end
end

View file

@ -0,0 +1,25 @@
module Vagrant
module LXC
class MachineState < Vagrant::MachineState
CREATED_STATES = %w( running stopped ).map!(&:to_sym)
def initialize(state_id)
short = state_id.to_s.gsub("_", " ")
long = I18n.t("vagrant.commands.status.#{state_id}")
super(state_id, short, long)
end
def created?
CREATED_STATES.include?(self.id)
end
def off?
self.id == :stopped
end
def running?
self.id == :running
end
end
end
end

View file

@ -1,51 +1,23 @@
require 'vagrant'
require "vagrant"
module Vagrant
module LXC
class Plugin < Vagrant.plugin("2")
name "vagrant-lxc"
name "Linux Containers (LXC) provider"
description <<-EOF
The LXC provider allows Vagrant to manage and control
LXC-based virtual machines.
EOF
provider(:lxc, parallel: true, priority: 7) do
require_relative 'provider'
init!
provider(:lxc) do
require File.expand_path("../provider", __FILE__)
Provider
end
command "lxc" do
require_relative 'command/root'
init!
Command::Root
end
config(:lxc, :provider) do
require_relative 'config'
init!
require File.expand_path("../config", __FILE__)
Config
end
synced_folder(:lxc) do
require_relative 'synced_folder'
SyncedFolder
end
provider_capability("lxc", "public_address") do
require_relative "provider/cap/public_address"
Provider::Cap::PublicAddress
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path(File.dirname(__FILE__) + '/../../locales/en.yml')
I18n.reload!
@_init = true
end
end
end
end

View file

@ -1,37 +1,21 @@
require "log4r"
require "vagrant-lxc/action"
require "vagrant-lxc/driver"
require "vagrant-lxc/container"
require "vagrant-lxc/machine_state"
module Vagrant
module LXC
class Provider < Vagrant.plugin("2", :provider)
attr_reader :driver
def self.usable?(raise_error=false)
if !Vagrant::Util::Platform.linux?
raise Errors::LxcLinuxRequired
end
true
end
attr_reader :container
def initialize(machine)
@logger = Log4r::Logger.new("vagrant::provider::lxc")
@machine = machine
ensure_lxc_installed!
machine_id_changed
end
def ensure_lxc_installed!
begin
SudoWrapper.new(privileged: @machine.provider_config.privileged).run("which", "lxc-create")
rescue Vagrant::LXC::Errors::ExecuteError
raise Errors::LxcNotInstalled
end
end
# If the machine ID changed, then we need to rebuild our underlying
# container.
def machine_id_changed
@ -39,9 +23,9 @@ module Vagrant
begin
@logger.debug("Instantiating the container for: #{id.inspect}")
@driver = Driver.new(id, privileged: @machine.provider_config.privileged)
@driver.validate!
rescue Driver::ContainerNotFound
@container = Container.new(id)
@container.validate!
rescue Container::NotFound
# The container doesn't exist, so we probably have a stale
# ID. Just clear the id out of the machine and reload it.
@logger.debug("Container not found! Clearing saved machine ID and reloading.")
@ -50,46 +34,35 @@ module Vagrant
end
end
# @see Vagrant::Plugin::V2::Provider#action
# @see Vagrant::Plugin::V1::Provider#action
def action(name)
# Attempt to get the action method from the Action class if it
# exists, otherwise return nil to show that we don't support the
# given action.
action_method = "action_#{name}"
# TODO: Rename to singular
return LXC::Action.send(action_method) if LXC::Action.respond_to?(action_method)
nil
end
# Returns the SSH info for accessing the Container.
def ssh_info
# If the Container is not running then we cannot possibly SSH into it, so
# If the Container is not created then we cannot possibly SSH into it, so
# we return nil.
return nil if state.id != :running
# Run a custom action called "ssh_ip" which does what it says and puts
# the IP found into the `:machine_ip` key in the environment.
env = @machine.action("ssh_ip")
# If we were not able to identify the container's IP, we return nil
# here and we let Vagrant core deal with it ;)
return nil unless env[:machine_ip]
return nil if state == :not_created
{
:host => env[:machine_ip],
:port => @machine.config.ssh.guest_port
:host => @container.assigned_ip,
:port => 22 # @driver.ssh_port(@machine.config.ssh.guest_port)
}
end
def state
state_id = nil
state_id = :not_created if !@driver.container_name
state_id = @driver.state if !state_id
state_id = :not_created if !@container.name
state_id = @container.state if !state_id
state_id = :unknown if !state_id
short = state_id.to_s.gsub("_", " ")
long = I18n.t("vagrant.commands.status.#{state_id}")
Vagrant::MachineState.new(state_id, short, long)
LXC::MachineState.new(state_id)
end
def to_s

View file

@ -1,17 +0,0 @@
module Vagrant
module LXC
class Provider
module Cap
module PublicAddress
def self.public_address(machine)
return nil if machine.state.id != :running
ssh_info = machine.ssh_info
return nil if !ssh_info
ssh_info[:host]
end
end
end
end
end
end

View file

@ -1,104 +0,0 @@
module Vagrant
module LXC
class SudoWrapper
# Include this so we can use `Subprocess` more easily.
include Vagrant::Util::Retryable
attr_reader :wrapper_path
def self.dest_path
"/usr/local/bin/vagrant-lxc-wrapper"
end
def initialize(privileged: true)
@wrapper_path = Pathname.new(SudoWrapper.dest_path).exist? && SudoWrapper.dest_path || nil
@privileged = privileged
@logger = Log4r::Logger.new("vagrant::lxc::sudo_wrapper")
end
def run(*command)
options = command.last.is_a?(Hash) ? command.last : {}
# Avoid running LXC commands with a restrictive umask.
# Otherwise disasters occur, like the container root directory
# having permissions `rwxr-x---` which prevents the `vagrant`
# user from accessing its own home directory; among other
# problems, SSH cannot then read `authorized_keys`!
old_mask = File.umask
File.umask(old_mask & 022) # allow all `r` and `x` bits
begin
if @privileged
if @wrapper_path && !options[:no_wrapper]
command.unshift @wrapper_path
execute *(['sudo'] + command)
else
execute *(['sudo', '/usr/bin/env'] + command)
end
else
execute *(['/usr/bin/env'] + command)
end
ensure
File.umask(old_mask)
end
end
private
# TODO: Review code below this line, it was pretty much a copy and
# paste from VirtualBox base driver and has no tests
def execute(*command, &block)
# Get the options hash if it exists
opts = {}
opts = command.pop if command.last.is_a?(Hash)
tries = 0
tries = 3 if opts[:retryable]
sleep = opts.fetch(:sleep, 1)
# Variable to store our execution result
r = nil
retryable(:on => LXC::Errors::ExecuteError, :tries => tries, :sleep => sleep) do
# Execute the command
r = raw(*command, &block)
# If the command was a failure, then raise an exception that is
# nicely handled by Vagrant.
if r.exit_code != 0
if @interrupted
raise LXC::Errors::SubprocessInterruptError, command.inspect
else
raise LXC::Errors::ExecuteError,
command: command.inspect, stderr: r.stderr, stdout: r.stdout, exitcode: r.exit_code
end
end
end
# Return the output, making sure to replace any Windows-style
# newlines with Unix-style.
stdout = r.stdout.gsub("\r\n", "\n")
if opts[:show_stderr]
{ :stdout => stdout, :stderr => r.stderr.gsub("\r\n", "\n") }
else
stdout
end
end
def raw(*command, &block)
int_callback = lambda do
@interrupted = true
@logger.info("Interrupted.")
end
# Append in the options for subprocess
command << { :notify => [:stdout, :stderr] }
Vagrant::Util::Busy.busy(int_callback) do
Vagrant::Util::Subprocess.execute(*command, &block)
end
end
end
end
end

View file

@ -1,72 +0,0 @@
module Vagrant
module LXC
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
def usable?(machine)
# These synced folders only work if the provider is LXC
machine.provider_name == :lxc
end
def prepare(machine, folders, _opts)
machine.ui.output(I18n.t("vagrant.actions.lxc.share_folders.preparing"))
# short guestpaths first, so we don't step on ourselves
folders = folders.sort_by do |id, data|
if data[:guestpath]
data[:guestpath].length
else
# A long enough path to just do this at the end.
10000
end
end
folders.each do |id, data|
host_path = Pathname.new(File.expand_path(data[:hostpath], machine.env.root_path))
guest_path = data[:guestpath]
machine.env.ui.warn(I18n.t("vagrant_lxc.messages.warn_owner")) if data[:owner]
machine.env.ui.warn(I18n.t("vagrant_lxc.messages.warn_group")) if data[:group]
if !host_path.directory? && data[:create]
# Host path doesn't exist, so let's create it.
@logger.info("Host path doesn't exist, creating: #{host_path}")
begin
host_path.mkpath
rescue Errno::EACCES
raise Vagrant::Errors::SharedFolderCreateFailed,
:path => hostpath.to_s
end
end
mount_opts = data[:mount_options]
machine.provider.driver.share_folder(host_path, guest_path, mount_opts)
# Guest path specified, so mount the folder to specified point
machine.ui.detail(I18n.t("vagrant.actions.vm.share_folders.mounting_entry",
guestpath: data[:guestpath],
hostpath: data[:hostpath],
guest_path: data[:guestpath]))
end
end
def enable(machine, folders, _opts)
# Emit an upstart event if we can
return unless machine.communicate.test("test -x /sbin/initctl")
# short guestpaths first, so we don't step on ourselves
folders = folders.sort_by do |id, data|
if data[:guestpath]
data[:guestpath].length
else
# A long enough path to just do this at the end.
10000
end
end
folders.each do |id, data|
guest_path = data[:guestpath]
machine.communicate.sudo(
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{guest_path}")
end
end
end
end
end

View file

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

9
lib/vendored_vagrant.rb Normal file
View file

@ -0,0 +1,9 @@
# Tks to: https://github.com/carlhuda/bundler/blob/master/lib/bundler/vendored_thor.rb
if defined?(Vagrant) && Vagrant.respond_to?(:in_installer?)
puts "vagrant has already been required. This may cause vagrant-lxc to malfunction in unexpected ways."
end
vendor = File.expand_path('../../vendor/vagrant/lib', __FILE__)
$:.unshift(vendor) unless $:.include?(vendor)
require 'vagrant'

View file

@ -1,82 +1,17 @@
en:
vagrant_lxc:
messages:
not_created: |-
The container hasn't been created yet.
not_running: |-
The container is not currently running.
will_not_destroy: |-
The container '%{name}' will not be destroyed, since the confirmation
was declined.
starting: |-
Starting container...
force_shutdown: |-
Forcing shutdown of container...
warn_networks: |-
Warning! The LXC provider doesn't support public networks, the settings
will be silently ignored.
warn_group: |-
Warning! The LXC provider doesn't support the :group parameter for synced
folders. It will be silently ignored.
warn_owner: |-
Warning! The LXC provider doesn't support the :owner parameter for synced
folders. It will be silently ignored.
setup_private_network: |-
Setting up private networks...
remove_bridge: |-
Removing bridge '%{name}'...
vagrant:
commands:
status:
stopped: |-
The container is currently stopped. Run `vagrant up` to bring it up again.
actions:
lxc:
compressing_rootfs: Compressing container's rootfs...
share_folders:
preparing: Setting up mount entries for shared folders...
errors:
lxc_interrupt_error: |-
Interrupted
lxc_execute_error: |-
There was an error executing %{command}
For more information on the failure, enable detailed logging by setting
the environment variable VAGRANT_LOG to DEBUG.
lxc_incompatible_box: |-
The base box you are trying to use is not compatible with the installed
vagrant-lxc version. Supported box versions are %{supported} but %{found} was found.
lxc_template_file_missing: |-
The template file used for creating the container was not found for %{name}
box.
lxc_templates_dir_missing: |-
Unable to identify lxc templates path.
Looked up under: %{paths}
lxc_linux_required: |-
The LXC provider only works on Linux. Please try to use
another provider.
lxc_not_installed: |-
The `lxc` package does not seem to be installed or `lxc-create` is not accessible on the PATH.
lxc_redir_not_installed: |-
`redir` is not installed or is not accessible on the PATH.
lxc_container_already_exists: |-
There is container on your system with the same name you've specified
on your Vagrantfile (%{name}), please choose a different one or
run `lxc-destroy --name %{name}` and try again.
lxc_command_not_supported: |-
Command (lxc-%{command}) not supported in version %{version}.
This command is available with version %{available_version}.

View file

@ -1,180 +0,0 @@
#!/usr/bin/env bash
# This is a modified version of /usr/share/lxc/templates/lxc-download
# that comes with ubuntu-lxc 1.0.0 stable from ppa changed to suit vagrant-lxc needs
#
# Copyright © 2014 Stéphane Graber <stgraber@ubuntu.com>
# Copyright © 2014 Fábio Rehm <fgrehm@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
# USA
set -eu
LXC_HOOK_DIR="/usr/share/lxc/hooks"
LXC_TEMPLATE_CONFIG="/usr/share/lxc/config"
LXC_MAPPED_GID=
LXC_MAPPED_UID=
LXC_NAME=
LXC_PATH=
LXC_ROOTFS=
LXC_TARBALL=
LXC_CONFIG=
LXC_USE_OLDCONFIG=
LXC_STRIP_COMPONENTS=2
usage() {
cat <<EOF
vagrant-lxc default template
Required arguments:
[ --tarball <path> ]: The full path of the rootfs tarball
Optional arguments:
[ --config ]: Configuration file to be used when building the container
[ --oldconfig ]: Use pre LXC 2.1 config format
[ -h | --help ]: This help message
LXC internal arguments (do not pass manually!):
[ --name <name> ]: The container name
[ --path <path> ]: The path to the container
[ --rootfs <rootfs> ]: The path to the container's rootfs
[ --mapped-uid <map> ]: A uid map (user namespaces)
[ --mapped-gid <map> ]: A gid map (user namespaces)
[ --strip-components <num> ]: Number of path components to strip from tarball
EOF
return 0
}
options=$(getopt -o h -l tarball:,config:,oldconfig,help:,name:,path:,rootfs:,mapped-uid:,mapped-gid:,strip-components: -- "$@")SS
if [ $? -ne 0 ]; then
usage $(basename $0)
exit 1
fi
eval set -- "$options"
while true
do
case "$1" in
-h|--help) usage $0 && exit 0;;
--config) LXC_CONFIG=$2; shift 2;;
--oldconfig) LXC_USE_OLDCONFIG=1; shift 1;;
--tarball) LXC_TARBALL=$2; shift 2;;
--name) LXC_NAME=$2; shift 2;;
--path) LXC_PATH=$2; shift 2;;
--rootfs) LXC_ROOTFS=$2; shift 2;;
--mapped-uid) LXC_MAPPED_UID=$2; shift 2;;
--mapped-gid) LXC_MAPPED_GID=$2; shift 2;;
--strip-components) LXC_STRIP_COMPONENTS=$2; shift 2;;
*) break;;
esac
done
if [ -z "${LXC_NAME}" ]; then
echo "'name' parameter is required"
exit 1
fi
if [ -z "${LXC_TARBALL}" ]; then
echo "'tarball' parameter is required"
exit 1
fi
if [ -z "${LXC_PATH}" ]; then
echo "'path' parameter is required"
exit 1
fi
# if $LXC_ROOTFS exists here, it was passed in with --rootfs
if [ -z "${LXC_ROOTFS}" ]; then
config=${LXC_PATH}/config
if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
LXC_ROOTFS=`grep 'lxc.rootfs =' $config | awk -F= '{ print $2 }'`
else
LXC_ROOTFS=$LXC_PATH/rootfs
echo "lxc.rootfs = ${LXC_ROOTFS}" >> $config
fi
fi
# Unpack the rootfs
echo "Unpacking the rootfs"
(
flock -x 200
if [ $? -ne 0 ]; then
echo "Cache repository is busy."
exit 1
fi
mkdir -p ${LXC_ROOTFS}
(cd ${LXC_ROOTFS} && tar xfz ${LXC_TARBALL} --strip-components=${LXC_STRIP_COMPONENTS} --xattrs --xattrs-include=* || true)
if [ ! -f ${LXC_ROOTFS}/bin/true ]; then
echo "Failed to extract rootfs"
exit 1
fi
) 200>${LXC_PATH}/vagrant_lock
rm ${LXC_PATH}/vagrant_lock
mkdir -p ${LXC_ROOTFS}/dev/pts/
## Extract all the network config entries
sed -i -e "/lxc.network/{w ${LXC_PATH}/config-network" -e "d}" \
${LXC_PATH}/config
## Extract any other config entry
sed -i -e "/lxc./{w ${LXC_PATH}/config-auto" -e "d}" ${LXC_PATH}/config
## Add the container-specific config
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# Container specific configuration (automatically set)" >> ${LXC_PATH}/config
if [ -e "${LXC_PATH}/config-auto" ]; then
cat ${LXC_PATH}/config-auto >> ${LXC_PATH}/config
rm ${LXC_PATH}/config-auto
fi
if [ $LXC_USE_OLDCONFIG ]; then
echo "lxc.utsname = ${LXC_NAME}" >> ${LXC_PATH}/config
else
echo "lxc.uts.name = ${LXC_NAME}" >> ${LXC_PATH}/config
fi
## Re-add the previously removed network config
if [ -e "${LXC_PATH}/config-network" ]; then
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# Network configuration (automatically set)" >> ${LXC_PATH}/config
cat ${LXC_PATH}/config-network >> ${LXC_PATH}/config
rm ${LXC_PATH}/config-network
fi
if [ -n "${LXC_CONFIG}" ]; then
## Append the defaults
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# vagrant-lxc base box specific configuration" >> ${LXC_PATH}/config
cat ${LXC_CONFIG} >> ${LXC_PATH}/config
fi
# Empty section for lxc.customize calls from vagrantfile
echo "" >> ${LXC_PATH}/config
echo "##############################################" >> ${LXC_PATH}/config
echo "# vagrant-lxc container specific configuration" >> ${LXC_PATH}/config
exit 0

View file

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

26
spec/Vagrantfile vendored
View file

@ -1,26 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.require_plugin 'vagrant-lxc'
Vagrant.require_plugin 'vagrant-cachier'
ENV['BOX_NAME'] ||= 'quantal64'
puts "Running vagrant commands using #{ENV['BOX_NAME']} box"
Vagrant.configure("2") do |config|
config.vm.box = ENV['BOX_NAME']
config.vm.hostname = 'lxc-test-box'
config.vm.box_url = ENV['BOX_URL']
config.vm.network :forwarded_port, guest: 80, host: 8080
config.cache.auto_detect = true
config.vm.provision :shell,
inline: 'mkdir -p /vagrant/tmp && echo -n "Provisioned" > /vagrant/tmp/provisioning'
config.vm.provision :shell,
inline: 'apt-get install apache2 -y'
config.vm.provision :shell, privileged: false,
inline: "if ! [ -f $HOME/original-box ]; then echo '#{ENV['BOX_NAME']}' > $HOME/original-box; fi"
end

4
spec/fixtures/sample-arp-output vendored Normal file
View file

@ -0,0 +1,4 @@
Address HWtype HWaddress Flags Mask Iface
120.0.3.30 ether 10:16:3e:c8:f9:b7 C lxcbr0
10.0.3.30 ether 00:16:3e:64:d6:74 C lxcbr0
192.168.25.1 ether 2c:e4:12:95:90:45 C wlan0

47
spec/fixtures/sample-config vendored Normal file
View file

@ -0,0 +1,47 @@
lxc.network.type=veth
lxc.network.link=lxcbr0
lxc.network.flags=up
lxc.network.hwaddr = 00:16:3e:64:d6:74
lxc.rootfs = /var/lib/lxc/a91df47bfc6e/rootfs
lxc.utsname = a91df47bfc6e
lxc.devttydir = lxc
lxc.tty = 4
lxc.pts = 1024
lxc.mount = /var/lib/lxc/a91df47bfc6e/fstab
lxc.arch = amd64
lxc.cap.drop = sys_module mac_admin mac_override
lxc.pivotdir = lxc_putold
# uncomment the next line to run the container unconfined:
#lxc.aa_profile = unconfined
lxc.cgroup.devices.deny = a
# Allow any mknod (but not using the node)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
#lxc.cgroup.devices.allow = c 4:0 rwm
#lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
#fuse
lxc.cgroup.devices.allow = c 10:229 rwm
#tun
lxc.cgroup.devices.allow = c 10:200 rwm
#full
lxc.cgroup.devices.allow = c 1:7 rwm
#hpet
lxc.cgroup.devices.allow = c 10:228 rwm
#kvm
lxc.cgroup.devices.allow = c 10:232 rwm

View file

@ -1,2 +0,0 @@
49: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 10.0.254.137/24 brd 10.0.254.255 scope global eth0

View file

@ -1,19 +1,24 @@
if ENV['COVERAGE']
require 'simplecov'
require 'coveralls'
SimpleCov.start { add_filter '/spec/' }
SimpleCov.merge_timeout 300
SimpleCov.start do
# This can probably go away once we stop using vagrant as submodule
add_filter { |source_file| source_file.filename =~ /\/vagrant\/plugins\// }
add_filter { |source_file| source_file.filename =~ /\/vagrant\/lib\/vagrant(\/|\.rb)/ }
add_filter { |source_file| source_file.filename =~ /\/spec\/support/ }
end
end
require 'bundler/setup'
require 'bundler'
require 'i18n'
require 'vagrant-lxc'
require 'rspec-spies'
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }
require 'rspec/fire'
RSpec::Fire.configure do |config|
config.verify_constant_names = ENV['VERIFY_CONSTANT_NAMES'] == '1'
end
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
@ -24,12 +29,4 @@ RSpec.configure do |config|
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'
config.mock_with :rspec do |c|
c.yield_receiver_to_any_instance_implementation_blocks = true
end
config.expect_with :rspec do |c|
c.syntax = :expect
end
config.raise_errors_for_deprecations!
end

View file

@ -0,0 +1,28 @@
module UnitExampleGroup
def self.included(base)
base.metadata[:type] = :unit
base.before do
Object.any_instance.stub(:system) { |*args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
Object.any_instance.stub(:`) { |*args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
require 'vagrant/util/subprocess'
Vagrant::Util::Subprocess.stub(:execute) { |*args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
end
end
def self.prevent_system_calls(*args, &block)
args.pop if args.last.is_a?(Hash)
raise <<-MSG
Somehow your code under test is trying to execute a command on your system,
please stub it out or move your spec code to an acceptance spec.
Command: "#{args.join(' ')}"
MSG
end
end

View file

@ -1,43 +0,0 @@
require 'unit_helper'
require 'tmpdir'
require 'vagrant-lxc/action/clear_forwarded_ports'
describe Vagrant::LXC::Action::ClearForwardedPorts do
let(:app) { double(:app, call: true) }
let(:env) { {machine: machine, ui: double(info: true)} }
let(:machine) { double(:machine, data_dir: data_dir) }
let!(:data_dir) { Pathname.new(Dir.mktmpdir) }
let(:pids_dir) { data_dir.join('pids') }
let(:pid) { 'a-pid' }
let(:pid_cmd) { 'redir' }
subject { described_class.new(app, env) }
before do
pids_dir.mkdir
pids_dir.join('redir_1234.pid').open('w') { |f| f.write(pid) }
subject.stub(system: true, :` => pid_cmd)
subject.call(env)
end
after { FileUtils.rm_rf data_dir.to_s }
it 'removes all files under pid directory' do
expect(Dir[pids_dir.to_s + "/redir_*.pid"]).to be_empty
end
context 'with a valid redir pid' do
it 'kills known processes' do
expect(subject).to have_received(:system).with("pkill -TERM -P #{pid}")
end
end
context 'with an invalid pid' do
let(:pid_cmd) { 'sudo ls' }
it 'does not kill the process' do
expect(subject).not_to have_received(:system).with("pkill -TERM -P #{pid}")
end
end
end

View file

@ -1,29 +0,0 @@
require 'unit_helper'
require 'vagrant-lxc/plugin'
require 'vagrant-lxc/provider'
require 'vagrant-lxc/action/compress_rootfs'
describe Vagrant::LXC::Action::CompressRootFS do
let(:app) { double(:app, call: true) }
let(:env) { {machine: machine, ui: double(info: true)} }
let(:machine) { double(Vagrant::Machine, provider: provider) }
let(:provider) { double(Vagrant::LXC::Provider, driver: driver) }
let(:driver) { double(Vagrant::LXC::Driver, compress_rootfs: compressed_rootfs_path) }
let(:compressed_rootfs_path) { '/path/to/rootfs.tar.gz' }
subject { described_class.new(app, env) }
before do
provider.stub_chain(:state, :id).and_return(:stopped)
subject.call(env)
end
it "asks the driver to compress container's rootfs" do
expect(driver).to have_received(:compress_rootfs)
end
it 'sets export.temp_dir on action env' do
expect(env['package.rootfs']).to eq(compressed_rootfs_path)
end
end

View file

@ -1,117 +0,0 @@
require 'unit_helper'
require 'tmpdir'
require 'vagrant-lxc/provider'
require 'vagrant-lxc/action/forward_ports'
describe Vagrant::LXC::Action::ForwardPorts do
let(:app) { double(:app, call: true) }
let(:env) { {machine: machine, ui: double(info: true, warn: true)} }
let(:machine) { double(:machine) }
let!(:data_dir) { Pathname.new(Dir.mktmpdir) }
let(:provider) { double(Vagrant::LXC::Provider, ssh_info: {host: container_ip}) }
let(:host_ip) { '127.0.0.1' }
let(:host_port) { 8080 }
let(:guest_port) { 80 }
let(:container_ip) { '10.0.1.234' }
let(:pid) { 'a-pid' }
let(:forward_conf) { {guest: guest_port, host: host_port, host_ip: host_ip} }
let(:networks) { [[:other_config, {}], [:forwarded_port, forward_conf]] }
subject { described_class.new(app, env) }
before do
machine.stub_chain(:config, :vm, :networks).and_return(networks)
machine.stub(provider: provider, data_dir: data_dir)
subject.stub(redir_version: 3)
subject.stub(exec: true)
subject.stub(spawn: pid)
end
after { FileUtils.rm_rf data_dir.to_s }
it 'forwards ports using redir' do
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n #{host_ip}:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 as default if host_ip is nil' do
forward_conf.delete(:host_ip)
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is a blank string' do
forward_conf[:host_ip] = ' '
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
it "stores redir pids on machine's data dir" do
subject.stub(system: true)
subject.call(env)
pid_file = data_dir.join('pids', "redir_#{host_port}.pid").read
expect(pid_file).to eq(pid)
end
it 'allows disabling a previously forwarded port' do
forward_conf[:disabled] = true
subject.stub(system: true)
subject.call(env)
expect(subject).not_to have_received(:spawn)
end
it 'uses redir 2.x command line interface' do
subject.stub(system: true)
subject.stub(redir_version: 2)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"redir --laddr=#{host_ip} --lport=#{host_port} --caddr=#{container_ip} --cport=#{guest_port} 2>/dev/null"
)
end
it 'raises RedirNotInstalled error if `redir` is not installed' do
subject.stub(system: false)
expect { subject.call(env) }.to raise_error(Vagrant::LXC::Errors::RedirNotInstalled)
end
context 'when a privileged port is used' do
let(:host_port) { 80 }
it 'forwards ports using redir' do
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n #{host_ip}:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is nil' do
forward_conf.delete(:host_ip)
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
it 'Uses 127.0.0.1 by default if host_ip is a blank string' do
forward_conf[:host_ip] = ' '
subject.stub(system: true)
subject.call(env)
expect(subject).to have_received(:spawn).with(
"sudo redir -n 127.0.0.1:#{host_port} #{container_ip}:#{guest_port} 2>/dev/null"
)
end
end
end

View file

@ -1,126 +1,44 @@
require 'unit_helper'
require 'vagrant'
require 'vagrant-lxc/errors'
require 'vagrant-lxc/action/base_action'
require 'vagrant-lxc/action/handle_box_metadata'
describe Vagrant::LXC::Action::HandleBoxMetadata do
let(:app) { double(:app, call: true) }
let(:env) { {machine: machine, ui: double(info: true, warn: true)} }
let(:machine) { double(:machine, box: box) }
let(:box) { double(:box, name: 'box-name', metadata: metadata, directory: box_directory) }
let(:metadata) { {'template-opts' => {'--foo' => 'bar'}} }
let(:box) { mock(:box, name: 'box-name', metadata: metadata, directory: box_directory) }
let(:box_directory) { Pathname.new('/path/to/box') }
let(:version) { '2' }
let(:metadata) { {'template-opts' => {'--foo' => 'bar'}, 'version' => version} }
let(:vagrant_key) { Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s }
let(:machine) { mock(:machine, box: box) }
let(:app) { mock(:app, call: true) }
let(:env) { {machine: machine, ui: stub(info: true)} }
let(:tmpdir) { '/tmp/rootfs/dir' }
subject { described_class.new(app, env) }
context 'with 1.0.0 box' do
let(:version) { '1.0.0' }
before do
File.stub(exists?: true)
# REFACTOR: This is pretty bad
subject.stub_chain(:template_config_file, :exist?).and_return(true)
subject.stub_chain(:template_config_file, :to_s).and_return(box_directory.join('lxc-config').to_s)
subject.call(env)
end
it 'sets the tarball argument for the template' do
expect(env[:lxc_template_opts]).to include(
'--tarball' => box_directory.join('rootfs.tar.gz').to_s
)
end
it 'sets the template --config parameter' do
expect(env[:lxc_template_opts]).to include(
'--config' => box_directory.join('lxc-config').to_s
)
end
it 'does not set the auth key argument for the template' do
expect(env[:lxc_template_opts]).not_to include(
'--auth-key' => vagrant_key
)
end
it 'sets the template options from metadata on env hash' do
expect(env[:lxc_template_opts]).to include(metadata['template-opts'])
end
xit 'sets the template source path on env hash' do
expect(env[:lxc_template_src]).to eq(box_directory.join('lxc-template').to_s)
end
it 'does not warn about deprecation' do
expect(env[:ui]).not_to have_received(:warn)
end
before do
Dir.stub(mktmpdir: tmpdir)
File.stub(exists?: true)
subject.stub(:system)
subject.call(env)
end
context 'with valid pre 1.0.0 box' do
before do
File.stub(exists?: true)
# REFACTOR: This is pretty bad
subject.stub_chain(:old_template_config_file, :exist?).and_return(true)
subject.stub_chain(:old_template_config_file, :to_s).and_return(box_directory.join('lxc.conf').to_s)
subject.call(env)
end
it 'sets the tarball argument for the template' do
expect(env[:lxc_template_opts]).to include(
'--tarball' => box_directory.join('rootfs.tar.gz').to_s
)
end
it 'sets the auth key argument for the template' do
expect(env[:lxc_template_opts]).to include(
'--auth-key' => vagrant_key
)
end
it 'sets the lxc config file parameter' do
expect(env[:lxc_template_config]).to eq(box_directory.join('lxc.conf').to_s)
end
it 'sets the template options from metadata on env hash' do
expect(env[:lxc_template_opts]).to include(metadata['template-opts'])
end
xit 'sets the template source path on env hash' do
expect(env[:lxc_template_src]).to eq(box_directory.join('lxc-template').to_s)
end
it 'warns about deprecation' do
expect(env[:ui]).to have_received(:warn)
end
it 'creates a tmp directory to store rootfs-cache-path' do
metadata['rootfs-cache-path'].should == tmpdir
end
describe 'with invalid contents' do
before { File.stub(exists?: true) }
it 'prepends vagrant and box name to template-name' do
metadata['template-name'].should == "vagrant-#{box.name}"
end
it 'validates box versions' do
%w( 2 3 1.0.0 ).each do |v|
metadata['version'] = v
expect { subject.call(env) }.to_not raise_error
end
it 'copies box template file to the right folder' do
src = box_directory.join('lxc-template').to_s
dest = "/usr/share/lxc/templates/lxc-#{metadata['template-name']}"
metadata['version'] = '1'
expect { subject.call(env) }.to raise_error
end
subject.should have_received(:system).
with("sudo su root -c \"cp #{src} #{dest}\"")
end
it 'raises an error if the rootfs tarball cant be found' do
allow(File).to receive(:exists?).with(box_directory.join('rootfs.tar.gz').to_s).and_return(false)
expect {
subject.call(env)
}.to raise_error(Vagrant::LXC::Errors::RootFSTarballMissing)
end
it 'does not raise an error if the lxc-template script cant be found' do
allow(File).to receive(:exists?).with(box_directory.join('lxc-template').to_s).and_return(false)
expect {
subject.call(env)
}.to_not raise_error
end
it 'extracts rootfs into a tmp folder' do
subject.should have_received(:system).
with(%Q[sudo su root -c "cd #{box_directory} && tar xfz rootfs.tar.gz -C #{tmpdir}"])
end
end

View file

@ -1,83 +0,0 @@
require 'unit_helper'
require 'vagrant-lxc/action/setup_package_files'
describe Vagrant::LXC::Action::SetupPackageFiles do
let(:app) { double(:app, call: true) }
let(:env) { {machine: machine, tmp_path: tmp_path, ui: double(info: true), 'package.rootfs' => rootfs_path} }
let(:machine) { double(Vagrant::Machine, box: box) }
let!(:tmp_path) { Pathname.new(Dir.mktmpdir) }
let(:box) { double(Vagrant::Box, directory: tmp_path.join('box')) }
let(:rootfs_path) { tmp_path.join('rootfs-amd64.tar.gz') }
subject { described_class.new(app, env) }
before do
box.directory.mkdir
files = %w( lxc-template metadata.json lxc.conf lxc-config ).map { |f| box.directory.join(f) }
(files + [rootfs_path]).each do |file|
file.open('w') { |f| f.puts file.to_s }
end
subject.stub(recover: true) # Prevents files from being removed on specs
end
after do
FileUtils.rm_rf(tmp_path.to_s)
end
context 'when all files exist' do
before { subject.call(env) }
it 'copies box lxc-template to package directory' do
expect(env['package.directory'].join('lxc-template')).to be_file
end
it 'copies metadata.json to package directory' do
expect(env['package.directory'].join('metadata.json')).to be_file
end
it 'copies box lxc.conf to package directory' do
expect(env['package.directory'].join('lxc-template')).to be_file
end
it 'copies box lxc-config to package directory' do
expect(env['package.directory'].join('lxc-config')).to be_file
end
it 'moves the compressed rootfs to package directory' do
expect(env['package.directory'].join(rootfs_path.basename)).to be_file
expect(env['package.rootfs']).not_to be_file
end
end
context 'when lxc-template file is not present' do
before do
box.directory.join('lxc-template').delete
end
it 'does not blow up' do
expect { subject.call(env) }.to_not raise_error
end
end
context 'when lxc.conf file is not present' do
before do
box.directory.join('lxc.conf').delete
end
it 'does not blow up' do
expect { subject.call(env) }.to_not raise_error
end
end
context 'when lxc-config file is not present' do
before do
box.directory.join('lxc-config').delete
end
it 'does not blow up' do
expect { subject.call(env) }.to_not raise_error
end
end
end

View file

@ -0,0 +1,148 @@
require 'unit_helper'
require "vendored_vagrant"
require 'vagrant-lxc/container/cli'
describe Vagrant::LXC::Container::CLI do
describe 'list' do
let(:lxc_ls_out) { "dup-container\na-container dup-container" }
let(:exec_args) { @exec_args }
let(:result) { @result }
before do
subject.stub(:run).with(:ls).and_return(lxc_ls_out)
@result = subject.list
end
it 'grabs previously created containers from lxc-ls output' do
result.should be_an Enumerable
result.should include 'a-container'
result.should include 'dup-container'
end
it 'removes duplicates from lxc-ls output' do
result.uniq.should == result
end
end
describe 'create' do
let(:template) { 'quantal-64' }
let(:name) { 'quantal-container' }
let(:template_args) { { '--extra-param' => 'param', '--other' => 'value' } }
subject { described_class.new(name) }
before do
subject.stub(:run)
subject.create(template, template_args)
end
it 'issues a lxc-create with provided template, container name and hash of arguments' do
subject.should have_received(:run).with(
:create,
'--template', template,
'--name', name,
'--',
'--extra-param', 'param',
'--other', 'value'
)
end
end
describe 'destroy' do
let(:name) { 'a-container-for-destruction' }
subject { described_class.new(name) }
before do
subject.stub(:run)
subject.destroy
end
it 'issues a lxc-destroy with container name' do
subject.should have_received(:run).with(:destroy, '--name', name)
end
end
describe 'start' do
let(:name) { 'a-container' }
subject { described_class.new(name) }
before do
subject.stub(:run)
end
it 'starts container on the background' do
subject.start
subject.should have_received(:run).with(
:start,
'-d',
'--name', name
)
end
it 'uses provided hash to configure the container' do
subject.start(['lxc.config=value', 'lxc.other=value'])
subject.should have_received(:run).with(:start, '-d', '--name', name,
'-s', 'lxc.config=value',
'-s', 'lxc.other=value'
)
end
end
describe 'shutdown' do
let(:name) { 'a-running-container' }
subject { described_class.new(name) }
before do
subject.stub(:run)
subject.shutdown
end
it 'issues a lxc-shutdown with provided container name' do
subject.should have_received(:run).with(:shutdown, '--name', name)
end
end
describe 'state' do
let(:name) { 'a-container' }
subject { described_class.new(name) }
before do
subject.stub(:run).and_return("state: STOPPED\npid: 2")
end
it 'calls lxc-info with the right arguments' do
subject.state
subject.should have_received(:run).with(:info, '--name', name)
end
it 'maps the output of lxc-info status out to a symbol' do
subject.state.should == :stopped
end
end
describe 'transition block' do
let(:name) { 'a-running-container' }
subject { described_class.new(name) }
before { subject.stub(:run) }
it 'yields cli object' do
subject.stub(:shutdown)
subject.transition_to(:stopped) { |c| c.shutdown }
subject.should have_received(:shutdown)
end
it 'throws an exception if block is not provided' do
expect {
subject.transition_to(:running)
}.to raise_error(described_class::TransitionBlockNotProvided)
end
it 'waits for the expected container state using lxc-wait' do
subject.transition_to(:running) { }
subject.should have_received(:run).with(:wait, '--name', name, '--state', 'RUNNING')
end
end
end

151
spec/unit/container_spec.rb Normal file
View file

@ -0,0 +1,151 @@
require 'unit_helper'
require "vendored_vagrant"
require 'vagrant-lxc/container'
describe Vagrant::LXC::Container do
let(:name) { nil }
subject { described_class.new(name) }
describe 'container name validation' do
let(:unknown_container) { described_class.new('unknown', cli) }
let(:valid_container) { described_class.new('valid', cli) }
let(:new_container) { described_class.new(nil) }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', list: ['valid']) }
it 'raises a NotFound error if an unknown container name gets provided' do
expect {
unknown_container.validate!
}.to raise_error(Vagrant::LXC::Container::NotFound)
end
it 'does not raise a NotFound error if a valid container name gets provided' do
expect {
valid_container.validate!
}.to_not raise_error(Vagrant::LXC::Container::NotFound)
end
it 'does not raise a NotFound error if nil is provider as name' do
expect {
new_container.validate!
}.to_not raise_error(Vagrant::LXC::Container::NotFound)
end
end
describe 'creation' do
let(:name) { 'random-container-name' }
let(:template_name) { 'template-name' }
let(:rootfs_cache) { '/path/to/cache' }
let(:public_key_path) { Vagrant.source_root.join('keys', 'vagrant.pub').expand_path.to_s }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', :create => true, :name= => true) }
subject { described_class.new(name, cli) }
before do
SecureRandom.stub(hex: name)
subject.create 'template-name' => template_name, 'rootfs-cache-path' => rootfs_cache, 'template-opts' => { '--foo' => 'bar'}
end
it 'creates container with the right arguments' do
cli.should have_received(:create).with(
template_name,
'--auth-key' => public_key_path,
'--cache' => rootfs_cache,
'--foo' => 'bar'
)
end
end
describe 'destruction' do
let(:name) { 'container-name' }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', destroy: true) }
subject { described_class.new(name, cli) }
before { subject.destroy }
it 'delegates to cli object' do
cli.should have_received(:destroy)
end
end
describe 'start' do
let(:config) { mock(:config, start_opts: ['a=1', 'b=2']) }
let(:name) { 'container-name' }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', start: true) }
subject { described_class.new(name, cli) }
before do
cli.stub(:transition_to).and_yield(cli)
end
it 'starts container with configured lxc settings' do
cli.should_receive(:start).with(['a=1', 'b=2'], nil)
subject.start(config)
end
it 'expects a transition to running state to take place' do
cli.should_receive(:transition_to).with(:running)
subject.start(config)
end
end
describe 'halt' do
let(:name) { 'container-name' }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', shutdown: true) }
subject { described_class.new(name, cli) }
before do
cli.stub(:transition_to).and_yield(cli)
end
it 'delegates to cli shutdown' do
cli.should_receive(:shutdown)
subject.halt
end
it 'expects a transition to running state to take place' do
cli.should_receive(:transition_to).with(:stopped)
subject.halt
end
end
describe 'state' do
let(:name) { 'random-container-name' }
let(:cli_state) { :something }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', state: cli_state) }
subject { described_class.new(name, cli) }
it 'delegates to cli' do
subject.state.should == cli_state
end
end
describe 'assigned ip' do
# This ip is set on the sample-arp-output fixture based on mac address from
# sample-config fixture
let(:ip) { "10.0.3.30" }
let(:conf_file_contents) { File.read('spec/fixtures/sample-config') }
let(:name) { 'random-container-name' }
context 'when container mac address gets returned from the first `arp` call' do
before do
@arp_output = File.read('spec/fixtures/sample-arp-output')
subject.stub(:raw) {
mock(stdout: "#{@arp_output}\n", exit_code: 0)
}
File.stub(read: conf_file_contents)
end
it 'gets parsed from `arp` based on lxc mac address' do
subject.assigned_ip.should == ip
subject.should have_received(:raw).with('arp', '-n')
end
end
pending 'when mac address is not returned from an `arp` call'
end
end

View file

@ -1,209 +0,0 @@
require 'unit_helper'
require 'vagrant-lxc/sudo_wrapper'
require 'vagrant-lxc/driver/cli'
describe Vagrant::LXC::Driver::CLI do
let(:sudo_wrapper) { double(Vagrant::LXC::SudoWrapper, run: true, wrapper_path: nil) }
subject { described_class.new(sudo_wrapper) }
describe 'list' do
let(:lxc_ls_out) { "dup-container\na-container dup-container" }
let(:result) { @result }
before do
allow(subject).to receive(:run).with(:ls).and_return(lxc_ls_out)
@result = subject.list
end
it 'grabs previously created containers from lxc-ls output' do
expect(result).to be_an Enumerable
expect(result).to include 'a-container'
expect(result).to include 'dup-container'
end
it 'removes duplicates from lxc-ls output' do
expect(result.uniq).to eq(result)
end
end
describe 'version' do
before do
allow(subject).to receive(:run).with(:create, '--version').and_return(lxc_version_out)
end
describe 'lxc version after 1.x.x' do
let(:lxc_version_out) { "1.0.0\n" }
it 'parses the version from the output' do
expect(subject.version).to eq('1.0.0')
end
end
end
describe 'config' do
before do
allow(subject).to receive(:run).with(:config, 'lxc.lxcpath').and_return(lxc_config_out)
allow(subject).to receive(:run).with(:create, '--version').and_return(lxc_version_out)
end
describe 'lxc version after 1.x.x'do
let(:lxc_config_out) { "/var/lib/lxc\n" }
let(:lxc_version_out) { "1.0.0\n" }
it 'parser the lxc.lxcpath value' do
expect(subject.config('lxc.lxcpath')).not_to end_with("\n")
end
end
end
describe 'create' do
let(:template) { 'quantal-64' }
let(:name) { 'quantal-container' }
let(:backingstore) { 'btrfs' }
let(:backingstore_opts) { [['--dir', '/tmp/foo'], ['--foo', 'bar']] }
let(:config_file) { 'config' }
let(:template_args) { { '--extra-param' => 'param', '--other' => 'value' } }
subject { described_class.new(sudo_wrapper, name) }
before do
allow(subject).to receive(:run) { |*args| @run_args = args }
end
it 'issues a lxc-create with provided template, container name and hash of arguments' do
subject.create(template, backingstore, backingstore_opts, config_file, template_args)
expect(subject).to have_received(:run).with(
:create,
'-B', backingstore,
'--template', template,
'--name', name,
*(backingstore_opts.flatten),
'-f', config_file,
'--',
'--extra-param', 'param',
'--other', 'value'
)
end
it 'wraps a low level error into something more meaningful in case the container already exists' do
allow(subject).to receive(:run) { raise Vagrant::LXC::Errors::ExecuteError, stderr: 'alreAdy Exists' }
expect {
subject.create(template, backingstore, backingstore_opts, config_file, template_args)
}.to raise_error(Vagrant::LXC::Errors::ContainerAlreadyExists)
end
end
describe 'destroy' do
let(:name) { 'a-container-for-destruction' }
subject { described_class.new(sudo_wrapper, name) }
before do
allow(subject).to receive(:run)
subject.destroy
end
it 'issues a lxc-destroy with container name' do
expect(subject).to have_received(:run).with(:destroy, '--name', name)
end
end
describe 'start' do
let(:name) { 'a-container' }
subject { described_class.new(sudo_wrapper, name) }
before do
allow(subject).to receive(:run)
end
it 'starts container on the background' do
subject.start
expect(subject).to have_received(:run).with(
:start,
'-d',
'--name', name
)
end
end
describe 'stop' do
let(:name) { 'a-running-container' }
subject { described_class.new(sudo_wrapper, name) }
before do
allow(subject).to receive(:run)
subject.stop
end
it 'issues a lxc-stop with provided container name' do
expect(subject).to have_received(:run).with(:stop, '--name', name)
end
end
describe 'state' do
let(:name) { 'a-container' }
subject { described_class.new(sudo_wrapper, name) }
before do
allow(subject).to receive(:run).and_return("state: STOPPED\npid: 2")
end
it 'calls lxc-info with the right arguments' do
subject.state
expect(subject).to have_received(:run).with(:info, '--name', name, retryable: true)
end
it 'maps the output of lxc-info status out to a symbol' do
expect(subject.state).to eq(:stopped)
end
it 'is not case sensitive' do
allow(subject).to receive(:run).and_return("StatE: STarTED\npid: 2")
expect(subject.state).to eq(:started)
end
end
describe 'attach' do
let(:name) { 'a-running-container' }
let(:command) { ['ls', 'cat /tmp/file'] }
let(:command_output) { 'folders list' }
subject { described_class.new(sudo_wrapper, name) }
before do
subject.stub(run: command_output)
end
it 'calls lxc-attach with specified command' do
subject.attach(*command)
expect(subject).to have_received(:run).with(:attach, '--name', name, '--', *command)
end
it 'supports a "namespaces" parameter' do
allow(subject).to receive(:run).with(:attach, '-h', :show_stderr => true).and_return({:stdout => '', :stderr => '--namespaces'})
subject.attach *(command + [{namespaces: ['network', 'mount']}])
expect(subject).to have_received(:run).with(:attach, '--name', name, '--namespaces', 'NETWORK|MOUNT', '--', *command)
end
end
describe 'transition block' do
before do
subject.stub(run: true, sleep: true, state: :stopped)
end
it 'yields a cli object' do
allow(subject).to receive(:shutdown)
subject.transition_to(:stopped) { |c| c.shutdown }
expect(subject).to have_received(:shutdown)
end
it 'throws an exception if block is not provided' do
expect {
subject.transition_to(:running)
}.to raise_error(described_class::TransitionBlockNotProvided)
end
skip 'waits for the expected container state'
end
end

View file

@ -1,257 +0,0 @@
require 'unit_helper'
require 'vagrant-lxc/driver'
require 'vagrant-lxc/driver/cli'
require 'vagrant-lxc/sudo_wrapper'
describe Vagrant::LXC::Driver do
describe 'container name validation' do
let(:unknown_container) { described_class.new('unknown', nil, cli) }
let(:valid_container) { described_class.new('valid', nil, cli) }
let(:new_container) { described_class.new(nil, nil) }
let(:cli) { double(Vagrant::LXC::Driver::CLI, list: ['valid']) }
it 'raises a ContainerNotFound error if an unknown container name gets provided' do
expect {
unknown_container.validate!
}.to raise_error
end
it 'does not raise a ContainerNotFound error if a valid container name gets provided' do
expect {
valid_container.validate!
}.not_to raise_error
end
it 'does not raise a ContainerNotFound error if nil is provider as name' do
expect {
new_container.validate!
}.not_to raise_error
end
end
describe 'creation' do
let(:name) { 'container-name' }
let(:backingstore) { 'btrfs' }
let(:backingstore_opts) { [['--dir', '/tmp/foo'], ['--foo', 'bar']] }
let(:template_name) { 'auto-assigned-template-id' }
let(:template_path) { '/path/to/lxc-template-from-box' }
let(:template_opts) { {'--some' => 'random-option'} }
let(:config_file) { '/path/to/lxc-config-from-box' }
let(:rootfs_tarball) { '/path/to/cache/rootfs.tar.gz' }
let(:cli) { double(Vagrant::LXC::Driver::CLI, :create => true, :name= => true) }
subject { described_class.new(nil, nil, cli) }
before do
allow(subject).to receive(:import_template).and_yield(template_name)
subject.create name, backingstore, backingstore_opts, template_path, config_file, template_opts
end
it 'sets the cli object container name' do
expect(cli).to have_received(:name=).with(name)
end
it 'creates container with the right arguments' do
expect(cli).to have_received(:create).with(
template_path,
backingstore,
backingstore_opts,
config_file,
template_opts
)
end
end
describe 'destruction' do
let(:cli) { double(Vagrant::LXC::Driver::CLI, destroy: true) }
subject { described_class.new('name', nil, cli) }
before { subject.destroy }
it 'delegates to cli object' do
expect(cli).to have_received(:destroy)
end
end
describe 'start' do
let(:customizations) { [['a', '1'], ['b', '2']] }
let(:internal_customization) { ['internal', 'customization'] }
let(:cli) { double(Vagrant::LXC::Driver::CLI, start: true) }
let(:sudo) { double(Vagrant::LXC::SudoWrapper) }
subject { described_class.new('name', sudo, cli) }
before do
sudo.should_receive(:run).with('cat', '/var/lib/lxc/name/config').exactly(2).times.
and_return('# CONFIGURATION')
sudo.should_receive(:run).twice.with('cp', '-f', %r{/(run|tmp)/.*}, '/var/lib/lxc/name/config')
sudo.should_receive(:run).twice.with('chown', 'root:root', '/var/lib/lxc/name/config')
expect(cli).to receive(:config).with("lxc.lxcpath").and_return("/var/lib/lxc")
subject.customizations << internal_customization
subject.start(customizations)
end
it 'prunes previous customizations before writing'
it 'writes configurations to config file'
it 'starts container with configured customizations' do
expect(cli).to have_received(:start)
end
end
describe 'halt' do
let(:cli) { double(Vagrant::LXC::Driver::CLI, stop: true) }
subject { described_class.new('name', nil, cli) }
before do
allow(cli).to receive(:transition_to).and_yield(cli)
end
it 'delegates to cli stop' do
expect(cli).to receive(:stop)
subject.forced_halt
end
it 'expects a transition to running state to take place' do
expect(cli).to receive(:transition_to).with(:stopped)
subject.forced_halt
end
it 'attempts to force the container to stop in case a shutdown doesnt work' do
allow(cli).to receive(:shutdown).and_raise(Vagrant::LXC::Driver::CLI::TargetStateNotReached.new :target, :source)
expect(cli).to receive(:transition_to).with(:stopped)
expect(cli).to receive(:stop)
subject.forced_halt
end
end
describe 'state' do
let(:cli_state) { :something }
let(:cli) { double(Vagrant::LXC::Driver::CLI, state: cli_state) }
subject { described_class.new('name', nil, cli) }
it 'delegates to cli' do
expect(subject.state).to eq(cli_state)
end
end
describe 'containers_path' do
let(:cli) { double(Vagrant::LXC::Driver::CLI, config: cli_config_value) }
subject { described_class.new('name', nil, cli) }
describe 'lxc version after 1.x.x' do
let(:cli_config_value) { '/etc/lxc' }
it 'delegates to cli' do
expect(subject.containers_path).to eq(cli_config_value)
end
end
end
describe 'folder sharing' do
let(:shared_folder) { {guestpath: '/vagrant', hostpath: '/path/to/host/dir'} }
let(:ro_rw_folder) { {guestpath: '/vagrant/ro_rw', hostpath: '/path/to/host/dir', mount_options: ['ro', 'rw']} }
let(:with_space_folder) { {guestpath: '/tmp/with space', hostpath: '/path/with space'} }
let(:folders) { [shared_folder, ro_rw_folder, with_space_folder] }
let(:expected_guest_path) { "vagrant" }
let(:sudo_wrapper) { double(Vagrant::LXC::SudoWrapper, run: true) }
let(:rootfs_path) { Pathname('/path/to/rootfs') }
subject { described_class.new('name', sudo_wrapper) }
describe "with fixed rootfs" do
before do
subject.stub(rootfs_path: Pathname('/path/to/rootfs'), system: true)
subject.share_folders(folders)
end
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
]
end
it 'supports additional mount options' do
expect(subject.customizations).to include [
'mount.entry',
"#{ro_rw_folder[:hostpath]} vagrant/ro_rw none ro,rw 0 0"
]
end
it 'supports directories with spaces' do
expect(subject.customizations).to include [
'mount.entry',
"/path/with\\040space tmp/with\\040space none bind,create=dir 0 0"
]
end
end
describe "with directory-based LXC config" do
let(:config_string) {
<<-ENDCONFIG.gsub(/^\s+/, '')
# Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4
lxc.pty.max = 1024
lxc.rootfs.path = #{rootfs_path}
# VAGRANT-BEGIN
lxc.network.type=veth
lxc.network.name=eth1
# VAGRANT-END
ENDCONFIG
}
before do
subject { described_class.new('name', sudo_wrapper) }
subject.stub(config_string: config_string)
subject.share_folders(folders)
end
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
]
end
end
describe "with overlayfs-based LXC config" do
let(:config_string) {
<<-ENDCONFIG.gsub(/^\s+/, '')
# Blah blah comment
lxc.mount.entry = proc proc proc nodev,noexec,nosuid 0 0
lxc.mount.entry = sysfs sys sysfs defaults 0 0
lxc.tty.max = 4
lxc.pty.max = 1024
lxc.rootfs.path = overlayfs:/path/to/master/directory:#{rootfs_path}
# VAGRANT-BEGIN
lxc.network.type=veth
lxc.network.name=eth1
# VAGRANT-END
ENDCONFIG
}
before do
subject { described_class.new('name', sudo_wrapper) }
subject.stub(config_string: config_string)
subject.share_folders(folders)
end
it 'adds a mount.entry to its local customizations' do
expect(subject.customizations).to include [
'mount.entry',
"#{shared_folder[:hostpath]} #{expected_guest_path} none bind,create=dir 0 0"
]
end
end
end
end

View file

@ -0,0 +1,39 @@
require 'unit_helper'
require 'vagrant-lxc/machine_state'
describe Vagrant::LXC::MachineState do
describe 'short description' do
subject { described_class.new(:not_created) }
it 'is a humanized version of state id' do
subject.short_description.should == 'not created'
end
end
describe 'long description' do
subject { described_class.new(:short_name) }
before { I18n.stub(t: 'some really long description') }
it 'is a localized version of the state id' do
subject.long_description.should == 'some really long description'
I18n.should have_received(:t).with('vagrant.commands.status.short_name')
end
end
context 'when state id is :running' do
subject { described_class.new(:running) }
it { should be_created }
it { should be_running }
it { should_not be_off }
end
context 'when state id is :stopped' do
subject { described_class.new(:stopped) }
it { should be_created }
it { should be_off }
it { should_not be_running }
end
end

View file

@ -1,38 +0,0 @@
module UnitExampleGroup
def self.included(base)
base.metadata[:type] = :unit
base.before do
allow_any_instance_of(Object).to receive(:system) { |instance, *args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
allow_any_instance_of(Object).to receive(:`) { |instance, *args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
allow_any_instance_of(Object).to receive(:exec) { |instance, *args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
allow_any_instance_of(Object).to receive(:fork) { |instance, *args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
allow_any_instance_of(Object).to receive(:spawn) { |instance, *args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
require 'vagrant/util/subprocess'
allow(Vagrant::Util::Subprocess).to receive(:execute) { |*args, &block|
UnitExampleGroup.prevent_system_calls(*args, &block)
}
end
end
def self.prevent_system_calls(*args, &block)
args.pop if args.last.is_a?(Hash)
raise <<-MSG
Somehow your code under test is trying to execute a command on your system,
please stub it out or move your spec code to an acceptance spec.
Block: #{block.inspect}
Command: "#{args.join(' ')}"
MSG
end
end

View file

@ -1,17 +1,9 @@
require 'spec_helper'
Dir[File.dirname(__FILE__) + "/unit/support/**/*.rb"].each { |f| require f }
if defined? SimpleCov
SimpleCov.command_name 'unit'
end
RSpec.configure do |config|
config.include RSpec::Fire
config.include UnitExampleGroup, :type => :unit, :example_group => {
:file_path => /\bspec\/unit\//
}
config.mock_with :rspec do |c|
c.yield_receiver_to_any_instance_implementation_blocks = true
end
end

17
tasks/boxes.rake Normal file
View file

@ -0,0 +1,17 @@
namespace :boxes do
namespace :quantal64 do
desc 'Build Ubuntu Quantal 64 bits Vagrant LXC box'
task :build do
if File.exists?('./boxes/output/lxc-quantal64.box')
puts 'Box has been built already!'
exit 1
end
sh 'mkdir -p boxes/output'
sh 'cd boxes/quantal64 && sudo ./download-ubuntu'
sh 'rm -f boxes/quantal64/rootfs.tar.gz'
sh 'cd boxes/quantal64 && sudo tar --numeric-owner -czf rootfs.tar.gz ./rootfs-amd64/*'
sh "cd boxes/quantal64 && sudo chown #{ENV['USER']}:#{ENV['USER']} rootfs.tar.gz && tar -czf ../output/lxc-quantal64.box ./* --exclude=rootfs-amd64 --exclude=download-ubuntu"
end
end
end

View file

@ -1,15 +1,12 @@
begin
require 'rspec/core/rake_task'
require 'coveralls/rake/task'
# TODO: add 'spec:acceptance' and 'spec:integration' then they are in place
desc 'Run all specs'
task :spec => ['spec:set_coverage', 'spec:unit', 'spec:acceptance']
task :spec => ['spec:unit']
desc 'Default task which runs all specs with code coverage enabled'
task :default => ['spec:set_coverage', 'spec:unit']
Coveralls::RakeTask.new
task :ci => ['spec:set_coverage', 'spec:unit', 'coveralls:push']
task :default => ['spec:set_coverage', 'spec']
rescue LoadError; end
namespace :spec do
@ -17,24 +14,17 @@ namespace :spec do
ENV['COVERAGE'] = 'true'
end
desc 'Run acceptance specs using vagrant-spec'
task :acceptance do
components = %w(
basic
network/forwarded_port
synced_folder
synced_folder/nfs
synced_folder/rsync
provisioner/shell
provisioner/puppet
provisioner/chef-solo
package
).map{|s| "provider/lxc/#{s}" }
sh "export ACCEPTANCE=true && bundle exec vagrant-spec test --components=#{components.join(' ')}"
def types
dirs = Dir['./spec/**/*_spec.rb'].map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') }.uniq
Hash[dirs.map { |d| [d.split('/').last, d] }]
end
types.each do |type, dir|
desc "Run the code examples in #{dir}"
RSpec::Core::RakeTask.new(type) do |t|
# Tells rspec-fire to verify if constants used really exist
ENV['VERIFY_CONSTANT_NAMES'] = '1'
desc "Run unit specs with rspec"
RSpec::Core::RakeTask.new(:unit) do |t|
t.pattern = "./unit/**/*_spec.rb"
t.pattern = "./#{dir}/**/*_spec.rb"
end
end
end

View file

@ -1,129 +0,0 @@
#!<%= cmd_paths['ruby'] %>
# Automatically created by vagrant-lxc
class Whitelist
class << self
def add(command, *args)
list[command] ||= []
list[command] << args
end
def add_regex(regex, *args)
regex_list << [regex, [args]]
end
def list
@list ||= {}
end
def regex_list
@regex_list ||= []
end
def allowed(command)
list[command] || allowed_regex(command) || []
end
def allowed_regex(command)
found = regex_list.find { |r| r[0] =~ command }
return found[1] if found
end
def run!(argv)
begin
command, args = `which #{argv.shift}`.chomp, argv || []
check!(command, args)
system "#{command} #{args.join(" ")}"
exit_code = $?.to_i
exit_code = 1 if exit_code == 256
exit exit_code
rescue => e
STDERR.puts e.message
exit 1
end
end
private
def check!(command, args)
allowed(command).each do |checks|
return if valid_args?(args, checks)
end
raise_invalid(command, args)
end
def valid_args?(args, checks)
return false unless valid_length?(args, checks)
check = nil
args.each_with_index do |provided, i|
check = checks[i] unless check == '**'
return false unless match?(provided, check)
end
true
end
def valid_length?(args, checks)
args.length == checks.length || checks.last == '**'
end
def match?(arg, check)
check == '**' || check.is_a?(Regexp) && !!check.match(arg) || arg == check
end
def raise_invalid(command, args)
raise "Invalid arguments for command #{command}, " <<
"provided args: #{args.inspect}"
end
end
end
base = "<%= lxc_base_path %>"
base_path = %r{\A#{base}/.*\z}
##
# Commands from provider.rb
# - Check lxc is installed
Whitelist.add '<%= cmd_paths['which'] %>', /\Alxc-\w+\z/
##
# Commands from driver.rb
# - Container config file
Whitelist.add '<%= cmd_paths['cat'] %>', base_path
# - Shared folders
Whitelist.add '<%= cmd_paths['mkdir'] %>', '-p', base_path
# - Container config customizations and pruning
Whitelist.add '<%= cmd_paths['cp'] %>', '-f', %r{/tmp/.*}, base_path
Whitelist.add '<%= cmd_paths['chown'] %>', 'root:root', base_path
# - Packaging
Whitelist.add '<%= cmd_paths['tar'] %>', '--numeric-owner', '-cvzf', %r{/tmp/.*/rootfs.tar.gz}, '-C', base_path, './rootfs'
Whitelist.add '<%= cmd_paths['chown'] %>', /\A\d+:\d+\z/, %r{\A/tmp/.*/rootfs\.tar\.gz\z}
# - Private network script and commands
Whitelist.add '<%= cmd_paths['ip'] %>', 'addr', 'add', /(\d+|\.)+\/24/, 'dev', /.+/
Whitelist.add '<%= cmd_paths['ip'] %>', 'link', 'set', /.+/, /(up|down)/
Whitelist.add '<%= cmd_paths['brctl'] %>', /(addbr|delbr)/, /.+/
Whitelist.add_regex %r{<%= pipework_regex %>}, '**'
##
# Commands from driver/cli.rb
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-version'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-ls'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-info', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-info', '--name', /.*/, '-iH'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-create', '-B', /.*/, '--template', /.*/, '--name', /.*/, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-create', '--version'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-destroy', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-start', '-d', '--name', /.*/, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-stop', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-shutdown', '--name', /.*/
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '--name', /.*/, '**'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-attach', '-h'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-config', 'lxc.lxcpath'
Whitelist.add '<%= cmd_paths['lxc_bin'] %>/lxc-update-config', '-c', /.*/
##
# Commands from driver/action/remove_temporary_files.rb
Whitelist.add '<%= cmd_paths['rm'] %>', '-rf', %r{\A#{base}/.*/rootfs/tmp/.*}
# Watch out for stones
Whitelist.run!(ARGV)

View file

@ -10,11 +10,23 @@ Gem::Specification.new do |gem|
gem.email = ["fgrehm@gmail.com"]
gem.description = %q{Linux Containers provider for Vagrant}
gem.summary = gem.description
gem.license = 'MIT'
gem.homepage = "https://github.com/fgrehm/vagrant-lxc"
gem.files = `git ls-files`.split($/)
gem.files = `git ls-files`.split($/)
gem.files << `cd vendor/vagrant && git ls-files`.split($/).map{|file| "vendor/vagrant/#{file}"}
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.add_development_dependency "rake"
# Vagrant's dependencies
gem.add_dependency "childprocess", "~> 0.3.7"
gem.add_dependency "erubis", "~> 2.7.0"
gem.add_dependency "i18n", "~> 0.6.0"
gem.add_dependency "json", ">= 1.5.1", "< 1.8.0"
gem.add_dependency "log4r", "~> 1.1.9"
gem.add_dependency "net-ssh", "~> 2.2.2"
gem.add_dependency "net-scp", "~> 1.0.4"
end

View file

@ -1,24 +0,0 @@
# FIXME: Figure out why this doesn't work
if ENV['COVERAGE'] == 'true'
require 'simplecov'
require 'coveralls'
SimpleCov.start { add_filter '/spec/' }
SimpleCov.command_name 'acceptance'
end
if ENV['BOX_PATH'] == nil
latest = ENV.fetch('LATEST_BOXES','2014-03-21')
release = ENV.fetch('RELEASE', 'acceptance')
local_path ="#{File.expand_path("../", __FILE__)}/boxes/output/#{latest}/vagrant-lxc-#{release}-amd64.box"
if File.exists?(local_path)
ENV['BOX_PATH'] = local_path
else
raise 'Set $BOX_PATH to the latest released boxes'
end
end
Vagrant::Spec::Acceptance.configure do |c|
c.component_paths << "spec/acceptance"
c.provider 'lxc', box: ENV['BOX_PATH'], features: ['!suspend']
end

1
vendor/vagrant vendored Submodule

@ -0,0 +1 @@
Subproject commit fc95944631a9adeed1c2b01f5b35c34ed8b59ff5