vagabund 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +32 -0
- data/Gemfile +13 -0
- data/README.md +301 -0
- data/Rakefile +1 -0
- data/Vagrantfile +50 -0
- data/lib/monkey_patches.rb +16 -0
- data/lib/vagabund/boxer/command.rb +202 -0
- data/lib/vagabund/boxer.rb +4 -0
- data/lib/vagabund/settler/config.rb +49 -0
- data/lib/vagabund/settler/errors.rb +48 -0
- data/lib/vagabund/settler/packages/base.rb +215 -0
- data/lib/vagabund/settler/packages/config.rb +39 -0
- data/lib/vagabund/settler/packages/package_config.rb +118 -0
- data/lib/vagabund/settler/packages.rb +101 -0
- data/lib/vagabund/settler/projects/base.rb +120 -0
- data/lib/vagabund/settler/projects/config.rb +51 -0
- data/lib/vagabund/settler/projects/project_config.rb +123 -0
- data/lib/vagabund/settler/projects/rails.rb +8 -0
- data/lib/vagabund/settler/projects/ruby.rb +38 -0
- data/lib/vagabund/settler/projects.rb +19 -0
- data/lib/vagabund/settler/provisioner.rb +34 -0
- data/lib/vagabund/settler/sources/git.rb +59 -0
- data/lib/vagabund/settler/sources/local.rb +28 -0
- data/lib/vagabund/settler/sources/url.rb +37 -0
- data/lib/vagabund/settler/sources.rb +10 -0
- data/lib/vagabund/settler.rb +4 -0
- data/lib/vagabund/squatter/config.rb +36 -0
- data/lib/vagabund/squatter/provisioner.rb +199 -0
- data/lib/vagabund/squatter/user.rb +56 -0
- data/lib/vagabund/squatter.rb +4 -0
- data/lib/vagabund/version.rb +3 -0
- data/lib/vagabund.rb +45 -0
- data/templates/locales/en.yml +15 -0
- data/vagabund.gemspec +24 -0
- metadata +121 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a48a7598698d3423b75512abc153f25e8ef0d54
|
4
|
+
data.tar.gz: 4915f37d0d22ced7d22afba5907c0b96477a8f33
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aaa07779e973b03fd0f1ee8b78c1952c68700089a6d84574649c0c2a944d8a24e299b2a927af30a2eea9c27e1f5028173c44ea3e19ba092bbbec0446a2352466
|
7
|
+
data.tar.gz: a949234531ac4aea039f48c0c3e4ed0579ef2d89e1409310a3c4a18d700ff6b168fd212ce56777c082d50bf100e2bc5e6a7984ed6ff0a1b64140ca7670e243d2
|
data/.gitignore
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
Gemfile.lock
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
.env
|
7
|
+
coverage
|
8
|
+
InstalledFiles
|
9
|
+
lib/bundler/man
|
10
|
+
pkg
|
11
|
+
rdoc
|
12
|
+
spec/reports
|
13
|
+
test/tmp
|
14
|
+
test/version_tmp
|
15
|
+
tmp
|
16
|
+
|
17
|
+
# Vagrant
|
18
|
+
.vagrant
|
19
|
+
*/.vagrant
|
20
|
+
*.box
|
21
|
+
*/*.box
|
22
|
+
|
23
|
+
# VIM swap files
|
24
|
+
*.swp
|
25
|
+
|
26
|
+
# YARD artifacts
|
27
|
+
.yardoc
|
28
|
+
_yardoc
|
29
|
+
doc/
|
30
|
+
|
31
|
+
# Test Packages
|
32
|
+
packages/
|
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem "vagrant", :git => "https://github.com/mitchellh/vagrant.git", :tag => "v1.7.2"
|
7
|
+
end
|
8
|
+
|
9
|
+
group :plugins do
|
10
|
+
gem "vagabund", path: "."
|
11
|
+
gem "vagrant-aws"
|
12
|
+
gem "vagrant-awsinfo"
|
13
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
Vagabund
|
2
|
+
========
|
3
|
+
|
4
|
+
Vagrant plugin providing automatic config management, git operations and the ability to checkout projects and manage services. Adds custom provisioners for "squatting" a machine by temporarily copying config files like your vim and git configs and provisioning a user account, and "settling" a machine by easily installing packages of all types and automatically checking out any projects you plan to work on. There is also the "boxer" component, which makes it easier to package and release custom boxes for different providers.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
### Squatter
|
9
|
+
|
10
|
+
Squatter is a provisioner that copies all your personal config files over to the VM automatically to make it feel more like home.
|
11
|
+
|
12
|
+
You can configure the host and guest home directories and override or add to the list of config files to be copied. Any relative paths provided will be relative to the home directories, while absolute paths will be preserved.
|
13
|
+
|
14
|
+
Really, this can be used to copy any files/directories from the host to the guest, not just "config files". Wildcard operators are not currently supported, but directories will be copied recursively. You can also provide a remote source in the form of a URL or S3 resource (the AWS CLI will be used to copy the file when using S3), or a `proc`.
|
15
|
+
|
16
|
+
The default list of config files includes `~/.vimrc`, `~/.viminfo`, `~/.gitconfig` and `~/.ssh/known_hosts`.
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
config.vm.provision :squat do |squatter|
|
20
|
+
squatter.host_home = '/Users/markrebec' # the host directory used for relative file paths
|
21
|
+
squatter.guest_home = '/home/vagrant' # the guest directory used for relatvie file paths
|
22
|
+
|
23
|
+
squatter.files = ['.vimrc', '.gitconfig', ['file.env', '.env']] # override the default list of files
|
24
|
+
squatter.file = '.filename' # home-relative path
|
25
|
+
squatter.file = '/path/to/.testfile' # absolute path
|
26
|
+
squatter.file = ['/host/path/.somefile', '.somefile'] # absolute host path, home-relative guest path
|
27
|
+
squatter.file = ['.somefile', '/guest/path/.somefile'] # home-relative host path, absolute guest path
|
28
|
+
squatter.file = 'http://example.com/source/file' # remote source file, home-relative guest path based of remote file basename
|
29
|
+
squatter.file = 's3://example-bucket/source/file' # remote source file, home-relative guest path based of remote file basename
|
30
|
+
squatter.file = ['http://example.com/source/file', '.other'] # remote source file, home-relative guest path
|
31
|
+
squatter.file = proc { ['.source_file', '.target_file'] } # must return a string or array, result will be interpreted like the above examples
|
32
|
+
squatter.file = [proc { '.source' }, proc { '.target' }] # must each return a string, results will be interpreted like the above examples
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
You can also use squatter to provision a new user account on the guest OS. This is particularly useful if you're creating a custom base box and want a user account other than the default `vagrant` user.
|
37
|
+
|
38
|
+
It's important to note that the files copied over by squatter are copied by the **current** user, not the user being created here. You can specify the guest home path to match your new user's home directory, but any files copied will be owned by the **current** ssh user.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
config.vm.provision :squat do |squatter|
|
42
|
+
# Create a user with the given username
|
43
|
+
squatter.user.username = 'example' # required
|
44
|
+
|
45
|
+
# Set the home directory for the user
|
46
|
+
squatter.user.home = '/home/example' # optional
|
47
|
+
|
48
|
+
# Set the primary group
|
49
|
+
squatter.user.group = 'example' # optional
|
50
|
+
|
51
|
+
# Set additional groups
|
52
|
+
squatter.user.groups = ['rvm', 'admins'] # optional
|
53
|
+
|
54
|
+
# Set the public key that should be added to .ssh/authorized_keys
|
55
|
+
# If not specified, the CURRENT user's authorized_keys file is copied over to the new user
|
56
|
+
# Add multiple keys by using an array
|
57
|
+
squatter.user.public_key = '/path/to/key.pub' # optional. a filepath, string or array of filepaths/strings
|
58
|
+
|
59
|
+
# Add an ssh config file for the user
|
60
|
+
squatter.user.ssh_config = '/path/to/.ssh/config' # optional. a filepath or string
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Settler
|
65
|
+
|
66
|
+
Settler is a provisioner that allows you to easily install software packages and checkout projects you'll be working on.
|
67
|
+
|
68
|
+
You can invoke the provisioner with the following in your `Vagrantfile`:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
config.vm.provision :settle do |settler|
|
72
|
+
# add software packages with settler.package and projects with settler.project
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
#### Packages
|
77
|
+
|
78
|
+
To build and install a software package on the guest OS you add them to your provisioner config:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
config.vm.provision :settle do |settler|
|
82
|
+
settler.packages do |packages|
|
83
|
+
# Download, extract and build the package using the built-in procs
|
84
|
+
package 'epubcheck', '3.0.1', url: 'https://github.com/IDPF/epubcheck/releases/download/v3.0.1/epubcheck-3.0.1.zip'
|
85
|
+
|
86
|
+
# Upload the local package, extract and build it using the built-in procs
|
87
|
+
package 'my_dependency', '0.3.0', local: '/local/path/to/mydependency-0.3.0.tar.gz'
|
88
|
+
end
|
89
|
+
|
90
|
+
# You can also add a package outside a packages config block
|
91
|
+
settler.package 'my_dependency', '0.3.0', local: '/local/path/to/mydependency-0.3.0.tar.gz'
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
The supported sources for pulling the package are: `git: GIT_URL`, `local: LOCAL_PATH`, `url: URL`.
|
96
|
+
|
97
|
+
You can override the paths where the work is done with a few configuration options, and you can use a block for an advanced configuration DSL:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
config.vm.provision :settle do |settler|
|
101
|
+
settler.packages do |packages|
|
102
|
+
package 'my_dependency', '0.3.0', local: '/local/path/to/mydependency-0.3.0.tar.gz' do |package|
|
103
|
+
package.build_root = '/some/local/path' # the path to which the source package will be pulled (default /tmp)
|
104
|
+
package.build_path = '/another/local/path/my_package' # the path where the package will be built (default build_root/name-version)
|
105
|
+
package.local_package = '/some/local/package.tar.gz' # the path to the local package file (default based on source filename)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
There is a built-in extractor that will work for most common file types and a builder and installer that perform simple `./configure && make` and `make install` commands, but you can override these as well as some other key hooks and actions with your own blocks/procs.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
config.vm.provision :settle do |settler|
|
115
|
+
settler.packages do |package|
|
116
|
+
package 'mydependency', '0.3.0', local: '/local/path/to/mydependency-0.3.0.tar.gz' do |pkg|
|
117
|
+
# Perform a custom action before provisioning the package
|
118
|
+
before do
|
119
|
+
installed = true # Maybe check something like `which some_binary`
|
120
|
+
|
121
|
+
# Allows skipping this package if it's already installed
|
122
|
+
skip true if installed
|
123
|
+
end
|
124
|
+
|
125
|
+
# Custom extractor to extract the pulled package into the build_path
|
126
|
+
extractor do |package, machine, channel|
|
127
|
+
execute "tar xzf #{local_package} /mydependency/src -C #{build_path}"
|
128
|
+
end
|
129
|
+
|
130
|
+
# Perform a custom build and install for the package
|
131
|
+
pkg.builder = proc do |package, machine, channel|
|
132
|
+
execute "cd #{build_path}; make"
|
133
|
+
end
|
134
|
+
|
135
|
+
install_proc = Proc.new do |package, machine, channel|
|
136
|
+
sudo "cp #{build_path}/my_custom_binary /usr/bin"
|
137
|
+
end
|
138
|
+
installer install_proc
|
139
|
+
|
140
|
+
# Or for simple commands, you can pass the command as a string (they will automatically be executed within the build_path)
|
141
|
+
builder "make"
|
142
|
+
installer "cp my_custom_binary /usr/bin", sudo: true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
```
|
147
|
+
There are a number of DSL methods that allow you to pass additional blocks/procs as package actions or before and after hooks, which will be executed within the context of the package instance:
|
148
|
+
|
149
|
+
* `before` - Executed before provisioning the package. Also aliased to `before_package`.
|
150
|
+
* `before_pull` - Executed before pulling the package from it's source.
|
151
|
+
* `puller` - Override the default behavior for pulling the remote source.
|
152
|
+
* `after_pull` - Executed after pulling the package.
|
153
|
+
* `before_extract` - Executed before extracting the package.
|
154
|
+
* `extractor` - Override the default extractor with your own.
|
155
|
+
* `after_extract` - Executed after extracting the package.
|
156
|
+
* `before_build` - Executed before building the package.
|
157
|
+
* `builder` - Override the default builder with your own.
|
158
|
+
* `after_build` - Executed after building the package.
|
159
|
+
* `before_install` - Executed before installing the package.
|
160
|
+
* `installer` - Override the default installer with your own.
|
161
|
+
* `after_install` - Executed after installing the package.
|
162
|
+
* `before_clean` - Executed before cleaning up.
|
163
|
+
* `cleaner` - Override the default cleaner with your own.
|
164
|
+
* `after_clean` - Executed after cleaning up.
|
165
|
+
* `after` - Executed after provisioning the package. Also aliased to `after_package`.
|
166
|
+
|
167
|
+
These each provide three arguments to the block, which are the package itself, the machine, and the channel (which is a shortcut for `machine.communicate`). However there are additionally a few helper methods available within these blocks to facilitate communication with the machine and input/output:
|
168
|
+
|
169
|
+
* Package: `build_root`, `build_path`, `local_package`
|
170
|
+
* Communication: `execute`, `sudo`, `test`
|
171
|
+
* I/O: `ask`, `detail`, `error`, `info`, `output`, `warn`
|
172
|
+
|
173
|
+
#### Projects
|
174
|
+
|
175
|
+
To automatically check out and prepare (i.e. run bundler for ruby projects) a project you can add a it to your provisioner config:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
config.vm.provision :settle do |settler|
|
179
|
+
settler.projects do |projects|
|
180
|
+
# A simple project, it will only be cloned into a local path
|
181
|
+
project 'my_project', git: 'git@github.com:example/example.git'
|
182
|
+
|
183
|
+
# A ruby or rails project will be bundled after it is cloned
|
184
|
+
project :ruby, 'ruby_project', git: 'git@github.com:example/example.git'
|
185
|
+
project :rails, 'my_app', git: 'git@github.com:example/example.git'
|
186
|
+
end
|
187
|
+
|
188
|
+
# You can also add a project outside a projects config block
|
189
|
+
settler.project 'my_project', git: 'git@github.com:example/example.git'
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
For ruby/rails projects your gem dependencies will be automatically installed for you with `bundle install`.
|
194
|
+
|
195
|
+
You can configure project paths for all or individual projects.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
config.vm.provision :settle do |settler|
|
199
|
+
settler.projects do |projects|
|
200
|
+
# All projects that do not override the path will be cloned into /some/local/path/PROJECT_NAME
|
201
|
+
projects.path = '/some/local/path'
|
202
|
+
|
203
|
+
# Project ends up in /some/local/path/my_project
|
204
|
+
project 'my_project', git: 'git@github.com:example/example.git'
|
205
|
+
|
206
|
+
# Project ends up in /another/path/other_project
|
207
|
+
project 'other_project', git: 'git@github.com:example/example.git', path: '/another/path/other_project'
|
208
|
+
|
209
|
+
# Project ends up in /another/path/third_project
|
210
|
+
project 'third_project', git: 'git@github.com:example/example.git', projects_path: '/another/path'
|
211
|
+
end
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
All config options (and some additional features) can also be called through the config DSL when passing a block.
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
config.vm.provision :settle do |settler|
|
219
|
+
settler.projects do |projects|
|
220
|
+
|
221
|
+
project :ruby, 'my_project', git: 'git@github.com:example/example.git' do |project_config|
|
222
|
+
# Project configuration DSL is available within an optional block
|
223
|
+
|
224
|
+
project_config.path = '/path/to/my_project'
|
225
|
+
|
226
|
+
# Do something before provisioning the project
|
227
|
+
before do |project, machine, channel|
|
228
|
+
info "Executing some command on the server..."
|
229
|
+
execute "cd #{path}; execute some command on the server"
|
230
|
+
end
|
231
|
+
|
232
|
+
# Do something before pulling the project
|
233
|
+
before_pull(Proc.new { sudo "execute something as root" })
|
234
|
+
|
235
|
+
# Do something after bundling the project
|
236
|
+
after_bundle "execute inline server command"
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
There are a number of DSL methods that allow you to pass additional blocks/procs as before and after hooks, which will be executed within the context of the project instance:
|
244
|
+
|
245
|
+
* `before` - Executed before provisioning the project. Also aliased to `before_project`.
|
246
|
+
* `before_pull` - Executed before pulling the project from it's source.
|
247
|
+
* `puller` - Override the default puller with your own.
|
248
|
+
* `after_pull` - Executed after pulling the project.
|
249
|
+
* `before_bundle` - Executed before running bundler (ruby/rails projects only).
|
250
|
+
* `bundler` - Override the default bundler with your own (ruby/rails projects only).
|
251
|
+
* `after_bundle` - Executed after running bundler (ruby/rails projects only).
|
252
|
+
* `after` - Executed after provisioning the project. Also aliased to `after_project`.
|
253
|
+
|
254
|
+
These each provide three arguments to the block, which are the project itself, the machine, and the channel (which is a shortcut for `machine.communicate`). However there are additionally a few helper methods available within these blocks to facilitate communication with the machine and input/output:
|
255
|
+
|
256
|
+
* Project: `path`
|
257
|
+
* Communication: `execute`, `sudo`, `test`
|
258
|
+
* I/O: `ask`, `detail`, `error`, `info`, `output`, `warn`
|
259
|
+
|
260
|
+
### Boxer
|
261
|
+
|
262
|
+
#### Requirements
|
263
|
+
|
264
|
+
If you plan on using boxer with aws, you'll need the [AWS Command Line Interface](http://aws.amazon.com/cli/) as well as the [vagrant-aws](https://github.com/mitchellh/vagrant-aws) and [vagrant-awsinfo](https://github.com/johntdyer/vagrant-awsinfo) vagrant plugins, which you can install with:
|
265
|
+
|
266
|
+
$ vagrant plugin install vagrant-aws
|
267
|
+
$ vagrant plugin install vagrant-awsinfo
|
268
|
+
|
269
|
+
#### Usage
|
270
|
+
|
271
|
+
Boxer is a new command for vagrant that wraps up the creation of boxes for VirtualBox and AWS. It uses the built in `vagrant package` to package VirtualBox VMs, and packages AWS instances by creating an AMI and a box that points to that AMI.
|
272
|
+
|
273
|
+
You can run boxer on a stopped VM with `vagrant boxer machine --name some-box-name`. Passing in a `machine` is optional, and without one all loaded machines will be boxed. The `--name` flag is also optional, and if not passed the name of the current machine (`default` by default) will be used.
|
274
|
+
|
275
|
+
**VirtualBox**
|
276
|
+
|
277
|
+
$ vagrant up --provider virtualbox
|
278
|
+
$ vagrant halt
|
279
|
+
$ vagrant boxer --name mybox-precise64-0.1.0
|
280
|
+
$ vagrant destroy -f
|
281
|
+
|
282
|
+
This will leave you with a vagrant box file backed by VirtualBox called `mybox-precise64-0.1.0-virtualbox.box` in the current directory.
|
283
|
+
|
284
|
+
**AWS**
|
285
|
+
|
286
|
+
$ vagrant up --provider aws
|
287
|
+
$ vagrant halt
|
288
|
+
$ vagrant boxer --name mybox-precise64-0.1.0
|
289
|
+
$ vagrant destroy -f
|
290
|
+
|
291
|
+
This will create an EC2 AMI named `mybox-precise64-0.1.0-aws` and leave you with a vagrant box file backed by AWS and pointing to the new AMI called `mybox-precise64-0.1.0-aws.box` in the current directory.
|
292
|
+
|
293
|
+
## Development
|
294
|
+
|
295
|
+
Make sure you read the documentation at [http://docs.vagrantup.com/v2/plugins/index.html](http://docs.vagrantup.com/v2/plugins/index.html) to familiarize yourself with basic usage and development practices for vagrant plugins.
|
296
|
+
|
297
|
+
**Note:** If you `bundle install` without specifying a `--path` the rubygems version of the `vagrant` binary might override your installed version, even outside of this project's directory. It is suggested you `bundle install --path ./.bundle` so you can use `bundle exec vagrant` while working on this plugin, but it won't interfere with your installed vagrant.
|
298
|
+
|
299
|
+
### Test Box
|
300
|
+
|
301
|
+
The default `Vagrantfile` points to the default `hashicorp/precise64` box and uses the VirtualBox provider. You can use any base box you'd like for development and testing, just edit the `Vagrantfile` to point to an available base box (**but do not commit your changes**).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/Vagrantfile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
require 'dotenv'
|
5
|
+
Dotenv.load
|
6
|
+
|
7
|
+
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
8
|
+
VAGRANTFILE_API_VERSION = "2"
|
9
|
+
|
10
|
+
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
11
|
+
|
12
|
+
# Example config for Squatter provisioner
|
13
|
+
config.vm.provision :squat do |squatter|
|
14
|
+
squatter.files = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# Example config for Settler provisioner
|
18
|
+
config.vm.provision :settle do |settler|
|
19
|
+
settler.packages do
|
20
|
+
end
|
21
|
+
settler.projects do
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
config.ssh.forward_agent = true
|
26
|
+
|
27
|
+
config.vm.define "vagabund-testing", primary: true do |machine|
|
28
|
+
machine.vm.provider "virtualbox" do |vb, override|
|
29
|
+
override.vm.box = "hashicorp/precise64"
|
30
|
+
|
31
|
+
vb.name = "vagabund-testing"
|
32
|
+
vb.memory = 2048
|
33
|
+
vb.cpus = 2
|
34
|
+
end
|
35
|
+
|
36
|
+
#machine.vm.provider :aws do |aws, override|
|
37
|
+
# override.vm.box = "aws/precise64"
|
38
|
+
# override.ssh.username = "ubuntu"
|
39
|
+
# override.ssh.private_key_path = "~/.ssh/id_rsa"
|
40
|
+
# override.vm.synced_folder "./", "/vagrant", disabled: true
|
41
|
+
|
42
|
+
# aws.access_key_id = ENV['AWS_ACCESS_KEY_ID']
|
43
|
+
# aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
|
44
|
+
# aws.keypair_name = ENV['AWS_KEYPAIR_NAME']
|
45
|
+
|
46
|
+
# aws.instance_type = "m1.large"
|
47
|
+
# aws.tags = {'Name' => 'vagabund-testing'}
|
48
|
+
#end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Ports over some useful stuff from ActiveSupport for method args
|
2
|
+
class Hash
|
3
|
+
def extractable_options?
|
4
|
+
true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Array
|
9
|
+
def extract_options!
|
10
|
+
if last.is_a?(Hash) && last.extractable_options?
|
11
|
+
pop
|
12
|
+
else
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Vagabund
|
5
|
+
module Boxer
|
6
|
+
class Command < Vagrant.plugin(2, :command)
|
7
|
+
def self.synopsis
|
8
|
+
"starts, provisions and packages a new vagrant box"
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(argv, env)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
options = Struct.new(:name)
|
17
|
+
|
18
|
+
opts = OptionParser.new do |o|
|
19
|
+
o.banner = "Usage: vagrant boxer [options] [name]"
|
20
|
+
o.separator ""
|
21
|
+
o.separator "Options:"
|
22
|
+
o.separator ""
|
23
|
+
|
24
|
+
o.on("--box-name NAME", "Name the box (and associated AMI for the aws provider)") do |value|
|
25
|
+
options.name = value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
argv = parse_options(opts)
|
30
|
+
return if !argv
|
31
|
+
#raise Vagrant::Errors::CLIInvalidUsage, help: opts.help.chomp if argv.empty?
|
32
|
+
|
33
|
+
with_target_vms(argv) do |machine|
|
34
|
+
box_name = "#{options.name || machine.name}-#{machine.provider_name}"
|
35
|
+
box_file = File.expand_path("./#{box_name}.box")
|
36
|
+
|
37
|
+
@env.ui.info "==> #{machine.name}: Packaging box for provider #{machine.provider_name}", bold: true
|
38
|
+
|
39
|
+
if box_exists?(box_file)
|
40
|
+
@env.ui.error "The file #{box_file} already exists. Please remove this file or specify a different box name."
|
41
|
+
next
|
42
|
+
end
|
43
|
+
|
44
|
+
if !created?(machine)
|
45
|
+
@env.ui.warn "==> #{machine.name}: Can't package VM that hasn't been created. Please create it with `vagrant up` and try again.", bold: true
|
46
|
+
next
|
47
|
+
end
|
48
|
+
|
49
|
+
send "package_#{machine.provider_name.to_s}", machine, box_name
|
50
|
+
end
|
51
|
+
|
52
|
+
0
|
53
|
+
end
|
54
|
+
|
55
|
+
def status(machine)
|
56
|
+
machine.state
|
57
|
+
end
|
58
|
+
|
59
|
+
def created?(machine)
|
60
|
+
status(machine).id != :not_created
|
61
|
+
end
|
62
|
+
|
63
|
+
def stopped?(machine)
|
64
|
+
[:poweroff, :stopped].include?(status(machine).id)
|
65
|
+
end
|
66
|
+
|
67
|
+
def stopping?(machine)
|
68
|
+
status(machine).id == :stopping
|
69
|
+
end
|
70
|
+
|
71
|
+
def running?(machine)
|
72
|
+
status(machine).id == :running
|
73
|
+
end
|
74
|
+
|
75
|
+
def package_virtualbox(machine, box_name, box_file=nil)
|
76
|
+
box_file ||= File.expand_path("./#{box_name}.box")
|
77
|
+
vm_name = machine.config.vm.get_provider_config(:virtualbox).name
|
78
|
+
|
79
|
+
if !stopped?(machine)
|
80
|
+
@env.ui.warn "==> #{machine.name}: Can't package running VM. Please stop it with `vagrant halt` and try again.", bold: true
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
|
84
|
+
@env.ui.info "==> #{vm_name}: Packaging VirtualBox VM '#{vm_name}' into #{File.basename(box_file)}", bold: true
|
85
|
+
system "vagrant package --base #{vm_name} --output #{box_file}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def package_aws(machine, box_name, box_file=nil)
|
89
|
+
box_file ||= File.expand_path("./#{box_name}.box")
|
90
|
+
|
91
|
+
if !stopped?(machine)
|
92
|
+
if stopping?(machine)
|
93
|
+
@env.ui.info "==> #{machine.name}: Waiting for VM to halt...", bold: true
|
94
|
+
instance_id = `vagrant awsinfo -m #{machine.name} -k instance_id`.chomp.split($/).last
|
95
|
+
wait_for_instance_state(instance_id)
|
96
|
+
else
|
97
|
+
@env.ui.warn "==> #{machine.name}: Can't package running VM. Please stop it with `vagrant halt` and try again.", bold: true
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
if aws_image_exists?(box_name)
|
103
|
+
image_id = existing_image_id(box_name)
|
104
|
+
@env.ui.warn "==> #{image_id}: An AMI with the name #{box_name} already exists", bold: true
|
105
|
+
@env.ui.warn " #{image_id}: Using existing AMI to package the box"
|
106
|
+
else
|
107
|
+
instance_id = `vagrant awsinfo -m #{machine.name} -k instance_id`.chomp.split($/).last
|
108
|
+
@env.ui.info "==> #{instance_id}: Packaging AWS instance into AMI #{box_name}...", bold: true
|
109
|
+
|
110
|
+
wait_for_instance_state instance_id, 'stopped'
|
111
|
+
image_id = `aws ec2 create-image --instance-id=#{instance_id} --name=#{box_name} --output=text`.chomp
|
112
|
+
end
|
113
|
+
|
114
|
+
@env.ui.info "==> #{image_id}: Creating AMI #{box_name}...", bold: true
|
115
|
+
wait_for_image_state image_id, 'available'
|
116
|
+
@env.ui.info "==> #{image_id}: Packaging AMI '#{box_name}' into #{File.basename(box_file)}", bold: true
|
117
|
+
|
118
|
+
Dir.mktmpdir do |dir|
|
119
|
+
Dir.chdir(dir) do
|
120
|
+
File.write 'Vagrantfile', aws_vagrantfile(image_id, box_name)
|
121
|
+
File.write 'metadata.json', aws_metafile
|
122
|
+
system "tar cf #{box_file} Vagrantfile metadata.json"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def box_exists?(box_file)
|
128
|
+
File.exists?(box_file)
|
129
|
+
end
|
130
|
+
|
131
|
+
def aws_image_exists?(box_name)
|
132
|
+
!existing_image_id(box_name).nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
def existing_image_id(box_name)
|
136
|
+
ami_json = JSON.parse(`aws ec2 describe-images --filters Name=name,Values=#{box_name}`.chomp)
|
137
|
+
ami_json['Images'].first['ImageId']
|
138
|
+
rescue
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def wait_for_image_state(image_id, state='available')
|
143
|
+
@env.ui.info " #{image_id}: Waiting for image to enter state '#{state}'..."
|
144
|
+
|
145
|
+
last_state = nil
|
146
|
+
while true do
|
147
|
+
ami_json = JSON.parse(`aws ec2 describe-images --owners=self --output=json`.chomp)
|
148
|
+
image = ami_json['Images'].select { |img| img['ImageId'] == image_id }.first
|
149
|
+
|
150
|
+
@env.ui.info " #{image_id}: Image is in state '#{image['State']}'" if image['State'] != last_state
|
151
|
+
|
152
|
+
last_state = image['State']
|
153
|
+
if image['State'] == 'failed'
|
154
|
+
@env.ui.error "==> #{image_id}: Image Creation Failed!", bold: true
|
155
|
+
@env.ui.error " #{image_id}: #{image['StateReason']['Message']}"
|
156
|
+
break;
|
157
|
+
elsif image['State'] == state
|
158
|
+
break;
|
159
|
+
end
|
160
|
+
|
161
|
+
sleep 5
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def wait_for_instance_state(instance_id, state='stopped')
|
166
|
+
# TODO use the machine to check/wait for state instead of `aws`?
|
167
|
+
@env.ui.info " #{instance_id}: Waiting for instance to enter state '#{state}'..."
|
168
|
+
|
169
|
+
last_state = nil
|
170
|
+
while true do
|
171
|
+
instance_state = JSON.parse(`aws ec2 describe-instances --instance-id=#{instance_id} --output=json --query="Reservations[0].Instances[0].State"`.chomp)
|
172
|
+
|
173
|
+
@env.ui.info " #{instance_id}: Instance is in state '#{instance_state['Name']}'" if instance_state['Name'] != last_state
|
174
|
+
|
175
|
+
last_state = instance_state['Name']
|
176
|
+
break if instance_state['Name'] == state
|
177
|
+
|
178
|
+
sleep 1
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def aws_vagrantfile(image_id, box_name)
|
183
|
+
return <<VAGRANTFILE
|
184
|
+
Vagrant.configure("2") do |config|
|
185
|
+
config.vm.provider :aws do |aws, override|
|
186
|
+
aws.ami = "#{image_id}" # #{box_name}
|
187
|
+
end
|
188
|
+
end
|
189
|
+
VAGRANTFILE
|
190
|
+
end
|
191
|
+
|
192
|
+
def aws_metafile
|
193
|
+
return <<METAFILE
|
194
|
+
{
|
195
|
+
"provider": "aws"
|
196
|
+
}
|
197
|
+
METAFILE
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'packages/config'
|
2
|
+
require_relative 'projects/config'
|
3
|
+
|
4
|
+
module Vagabund
|
5
|
+
module Settler
|
6
|
+
class Config < Vagrant.plugin(2, :config)
|
7
|
+
def packages(*args, &block)
|
8
|
+
@packages ||= Packages::Config.new(self, *args)
|
9
|
+
@packages.instance_eval &block if block_given?
|
10
|
+
@packages
|
11
|
+
end
|
12
|
+
|
13
|
+
def packages=(pkgs)
|
14
|
+
raise Vagrant::Errors::VagrantError, :invalid_packages_config unless pkgs.is_a?(Array)
|
15
|
+
@packages = Packages::Config.new(self, pkgs)
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_package(*args, &block)
|
19
|
+
packages.add_package *args, &block
|
20
|
+
end
|
21
|
+
alias_method :package, :add_package
|
22
|
+
alias_method :package=, :add_package
|
23
|
+
|
24
|
+
def projects(*args, &block)
|
25
|
+
@projects ||= Projects::Config.new(self, *args)
|
26
|
+
@projects.instance_eval &block if block_given?
|
27
|
+
@projects
|
28
|
+
end
|
29
|
+
|
30
|
+
def projects=(prjs)
|
31
|
+
raise Vagrant::Errors::VagrantError, :invalid_projects_config unless prjs.is_a?(Array)
|
32
|
+
@projects = Projects::Config.new(self, prjs)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_project(*args, &block)
|
36
|
+
projects.add_project *args, &block
|
37
|
+
end
|
38
|
+
alias_method :project, :add_project
|
39
|
+
alias_method :project=, :add_project
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
super
|
43
|
+
Dir['packages/**/*.rb'].each { |package| eval(IO.read(package)) }
|
44
|
+
Dir['projects/**/*.rb'].each { |project| eval(IO.read(project)) }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|