vagabund 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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,4 @@
1
+ module Vagabund
2
+ module Boxer
3
+ end
4
+ 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