fragrant 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +22 -0
- data/README.md +6 -0
- data/bin/fragrant +14 -0
- data/lib/fragrant/address_manager.rb +80 -0
- data/lib/fragrant/vagrantfile_generator.rb +64 -0
- data/lib/fragrant.rb +323 -0
- data/spec/fragrant/environments_spec.rb +18 -0
- data/spec/fragrant/vagrantfile_generator_spec.rb +40 -0
- data/spec/fragrant/vms_spec.rb +26 -0
- data/spec/spec_helper.rb +15 -0
- data/vendor/grape/LICENSE +20 -0
- data/vendor/grape/lib/grape/api.rb +420 -0
- data/vendor/grape/lib/grape/cookies.rb +41 -0
- data/vendor/grape/lib/grape/endpoint.rb +377 -0
- data/vendor/grape/lib/grape/entity.rb +378 -0
- data/vendor/grape/lib/grape/exceptions/base.rb +17 -0
- data/vendor/grape/lib/grape/exceptions/validation_error.rb +10 -0
- data/vendor/grape/lib/grape/middleware/auth/basic.rb +30 -0
- data/vendor/grape/lib/grape/middleware/auth/digest.rb +30 -0
- data/vendor/grape/lib/grape/middleware/auth/oauth2.rb +72 -0
- data/vendor/grape/lib/grape/middleware/base.rb +154 -0
- data/vendor/grape/lib/grape/middleware/error.rb +87 -0
- data/vendor/grape/lib/grape/middleware/filter.rb +17 -0
- data/vendor/grape/lib/grape/middleware/formatter.rb +81 -0
- data/vendor/grape/lib/grape/middleware/prefixer.rb +21 -0
- data/vendor/grape/lib/grape/middleware/versioner/header.rb +59 -0
- data/vendor/grape/lib/grape/middleware/versioner/param.rb +44 -0
- data/vendor/grape/lib/grape/middleware/versioner/path.rb +42 -0
- data/vendor/grape/lib/grape/middleware/versioner.rb +29 -0
- data/vendor/grape/lib/grape/route.rb +23 -0
- data/vendor/grape/lib/grape/util/deep_merge.rb +23 -0
- data/vendor/grape/lib/grape/util/hash_stack.rb +100 -0
- data/vendor/grape/lib/grape/validations/coerce.rb +61 -0
- data/vendor/grape/lib/grape/validations/presence.rb +11 -0
- data/vendor/grape/lib/grape/validations/regexp.rb +13 -0
- data/vendor/grape/lib/grape/validations.rb +192 -0
- data/vendor/grape/lib/grape/version.rb +3 -0
- data/vendor/grape/lib/grape.rb +44 -0
- metadata +216 -0
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2012 Matt Whiteley
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/bin/fragrant
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
lib_dir = File.expand_path("../../lib", __FILE__)
|
5
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
6
|
+
require 'fragrant'
|
7
|
+
require 'rack/handler/puma'
|
8
|
+
require 'vegas'
|
9
|
+
|
10
|
+
app = Fragrant::Frontend.new
|
11
|
+
def app.server
|
12
|
+
"puma"
|
13
|
+
end
|
14
|
+
Vegas::Runner.new(app, 'fragrant', {:skip_launch => true, :app_dir => Fragrant.env_dir})
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Fragrant
|
5
|
+
AddressRangeExhausted = Class.new(StandardError)
|
6
|
+
|
7
|
+
class AddressManager
|
8
|
+
attr_accessor :data_location, :allocated_addresses
|
9
|
+
attr_accessor :address_range, :address_map
|
10
|
+
|
11
|
+
def initialize(data_location, address_range)
|
12
|
+
self.data_location = data_location
|
13
|
+
self.address_range = address_range
|
14
|
+
self.address_map = {}
|
15
|
+
self.allocated_addresses = []
|
16
|
+
load_address_data
|
17
|
+
end
|
18
|
+
|
19
|
+
def load_address_data
|
20
|
+
return unless File.exist?(data_location)
|
21
|
+
unless File.writable?(data_location)
|
22
|
+
raise "Unable to access IP address config file at #{data_location}"
|
23
|
+
end
|
24
|
+
File.open(data_location, 'rb') do |f|
|
25
|
+
data = JSON.parse(f.read)
|
26
|
+
self.address_range = data['address_range']
|
27
|
+
self.address_map = data['address_map']
|
28
|
+
self.allocated_addresses = data['allocated_addresses']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def address_data
|
33
|
+
{:address_range => address_range,
|
34
|
+
:allocated_addresses => allocated_addresses,
|
35
|
+
:address_map => address_map}
|
36
|
+
end
|
37
|
+
|
38
|
+
def first_available_address
|
39
|
+
ip = IPAddr.new(address_range).to_range.detect do |ip|
|
40
|
+
!allocated_addresses.include?(ip.to_s)
|
41
|
+
end
|
42
|
+
return ip.to_s if ip
|
43
|
+
raise AddressRangeExhausted, "No more addresses available in range #{address_range}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def persist
|
47
|
+
dir = File.dirname(data_location)
|
48
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
49
|
+
File.open(data_location, 'wb') do |f|
|
50
|
+
f.write(address_data.to_json)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def claim_address(environment_id)
|
55
|
+
if address_map.key?(environment_id)
|
56
|
+
raise "#{environment_id} already has an address"
|
57
|
+
end
|
58
|
+
address = first_available_address
|
59
|
+
address_map[environment_id] = address
|
60
|
+
allocated_addresses << address
|
61
|
+
persist
|
62
|
+
address
|
63
|
+
end
|
64
|
+
|
65
|
+
def release_address(environment_id, ip)
|
66
|
+
unless address_map.key?(environment_id)
|
67
|
+
raise "No addresses registered to environment #{environment_id}"
|
68
|
+
end
|
69
|
+
|
70
|
+
address = address_map[environment_id]
|
71
|
+
unless address == ip
|
72
|
+
raise "IP #{ip} is not currently assigned to environment #{environment_id} (#{address} is)"
|
73
|
+
end
|
74
|
+
|
75
|
+
address_map.delete environment_id
|
76
|
+
allocated_addresses.delete ip
|
77
|
+
persist
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Fragrant
|
5
|
+
class VagrantfileGenerator
|
6
|
+
|
7
|
+
attr_accessor :addresses
|
8
|
+
|
9
|
+
def self.template_path
|
10
|
+
File.expand_path('../../templates/Vagrantfile', __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(target_directory, opts={})
|
14
|
+
@target_directory = target_directory
|
15
|
+
@scripts = opts[:scripts] || []
|
16
|
+
@addresses = opts[:addresses] || []
|
17
|
+
@box_name = opts[:box_name]
|
18
|
+
@box_url = opts[:box_url]
|
19
|
+
@contents = opts[:contents]
|
20
|
+
end
|
21
|
+
|
22
|
+
def box_name
|
23
|
+
@box_name || "precise32"
|
24
|
+
end
|
25
|
+
|
26
|
+
def box_url
|
27
|
+
@box_url || "http://files.vagrantup.com/precise32.box"
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_script(contents)
|
31
|
+
raise "body set, adding a script not supported" if @contents
|
32
|
+
@scripts << contents
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def writeable_scripts
|
37
|
+
retval = {}
|
38
|
+
@scripts.each_with_index do |contents, i|
|
39
|
+
retval[sprintf("script%03d", i + 1)] = contents
|
40
|
+
end
|
41
|
+
retval
|
42
|
+
end
|
43
|
+
|
44
|
+
def write
|
45
|
+
FileUtils.mkdir_p(@target_directory)
|
46
|
+
|
47
|
+
writeable_scripts.each do |filename, script_body|
|
48
|
+
File.open(File.join(@target_directory, filename), 'w') do |f|
|
49
|
+
f.print script_body
|
50
|
+
f.chmod 0755
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
@contents ||= Vagrant::Util::TemplateRenderer.render(self.class.template_path,
|
55
|
+
:box_name => box_name,
|
56
|
+
:box_url => box_url,
|
57
|
+
:provision_script_paths => writeable_scripts.keys.sort,
|
58
|
+
:addresses => addresses)
|
59
|
+
File.open(File.join(@target_directory, 'Vagrantfile'), 'w') { |f| f.print @contents }
|
60
|
+
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/fragrant.rb
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
vendor_dir = File.expand_path('../../vendor', __FILE__)
|
2
|
+
$LOAD_PATH.unshift File.join(vendor_dir, 'grape', 'lib')
|
3
|
+
require 'grape'
|
4
|
+
require 'thread'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'uuid'
|
7
|
+
require 'vagrant'
|
8
|
+
require 'fragrant/vagrantfile_generator'
|
9
|
+
require 'fragrant/address_manager'
|
10
|
+
|
11
|
+
module Fragrant
|
12
|
+
def self.env_dir
|
13
|
+
@env_dir ||= begin
|
14
|
+
dir = ENV["FRAGRANT_ENV_DIR"] || File.expand_path("~/.fragrant")
|
15
|
+
FileUtils.mkdir_p(dir)
|
16
|
+
dir
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.address_manager
|
21
|
+
data_location = File.join(Fragrant.env_dir, "addresses.json")
|
22
|
+
range = ENV["FRAGRANT_IP_RANGE"] || "172.24.24.128/25"
|
23
|
+
@address_manager ||= AddressManager.new(data_location, range)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.add_task(task)
|
27
|
+
background_worker
|
28
|
+
tasks.push(task)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Tasks are two-element Arrays of a machine id and a set of vagrant args
|
32
|
+
def self.tasks
|
33
|
+
@tasks ||= Queue.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.create_worker_thread
|
37
|
+
thread = Thread.new do
|
38
|
+
Thread.current.abort_on_exception = true
|
39
|
+
until Thread.current[:shutdown] do
|
40
|
+
unless Fragrant.tasks.empty?
|
41
|
+
task = Fragrant.tasks.pop
|
42
|
+
env = Vagrant::Environment.new({ :cwd => File.join(env_dir, task[:id]) })
|
43
|
+
env.cli(task[:args])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
at_exit do
|
49
|
+
$stderr.puts "Waiting for any running Vagrant tasks to complete."
|
50
|
+
thread[:shutdown] = true
|
51
|
+
thread.join
|
52
|
+
end
|
53
|
+
thread
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.background_worker
|
57
|
+
@background_worker ||= create_worker_thread
|
58
|
+
end
|
59
|
+
|
60
|
+
class Frontend < Grape::API
|
61
|
+
version 'v1', :using => :header, :vendor => 'fragrant'
|
62
|
+
format :json
|
63
|
+
|
64
|
+
ENV_REGEX = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/
|
65
|
+
|
66
|
+
rescue_from :all do |e|
|
67
|
+
rack_response({ :message => "Encountered exception: #{e}", :backtrace => e.backtrace }, 500, {"Content-Type" => "application/json"})
|
68
|
+
end
|
69
|
+
|
70
|
+
helpers do
|
71
|
+
def add_task(task)
|
72
|
+
Fragrant.add_task(task)
|
73
|
+
end
|
74
|
+
|
75
|
+
def box_name
|
76
|
+
params[:box_name] || 'precise32'
|
77
|
+
end
|
78
|
+
|
79
|
+
def box_url
|
80
|
+
params[:box_url] || "http://files.vagrantup.com/precise32.box"
|
81
|
+
end
|
82
|
+
|
83
|
+
def user_script
|
84
|
+
params[:user_data_script]
|
85
|
+
end
|
86
|
+
|
87
|
+
def env_dir
|
88
|
+
Fragrant.env_dir
|
89
|
+
end
|
90
|
+
|
91
|
+
def env_glob
|
92
|
+
Dir.entries(env_dir).select do |d|
|
93
|
+
next if d.start_with?('.')
|
94
|
+
File.exists?(File.join(env_dir, d, 'Vagrantfile'))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def env_rand
|
99
|
+
UUID.generate
|
100
|
+
end
|
101
|
+
|
102
|
+
def v_action
|
103
|
+
route.route_path.split(/[\/\(]/)[2]
|
104
|
+
end
|
105
|
+
|
106
|
+
def v_env(id = params[:id])
|
107
|
+
Vagrant::Environment.new({ :cwd => File.join(env_dir, id) })
|
108
|
+
end
|
109
|
+
|
110
|
+
def allocate_address(env_id)
|
111
|
+
Fragrant.address_manager.claim_address(env_id)
|
112
|
+
end
|
113
|
+
|
114
|
+
def v_file(env_id, directory, contents = nil)
|
115
|
+
addresses = [allocate_address(env_id)] unless contents
|
116
|
+
VagrantfileGenerator.new(directory, :box_name => box_name,
|
117
|
+
:box_url => box_url,
|
118
|
+
:scripts => Array(user_script),
|
119
|
+
:addresses => addresses,
|
120
|
+
:contents => contents).write
|
121
|
+
Array(addresses)
|
122
|
+
end
|
123
|
+
|
124
|
+
def make_machine_dir(machine_id)
|
125
|
+
machine_dir = File.join(env_dir, machine_id)
|
126
|
+
begin
|
127
|
+
Dir.mkdir(machine_dir, 0755)
|
128
|
+
rescue Errno::EEXIST
|
129
|
+
error!({ "error" => "#{machine_dir} already exists!" }, 409)
|
130
|
+
end
|
131
|
+
machine_dir
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
get do
|
136
|
+
{}
|
137
|
+
end
|
138
|
+
|
139
|
+
resource :environments do
|
140
|
+
|
141
|
+
desc "Destroys a Vagrant environment"
|
142
|
+
params do
|
143
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
144
|
+
optional :vm_name, :desc => 'single vm to act on'
|
145
|
+
end
|
146
|
+
delete '/destroy/:id' do
|
147
|
+
args = [v_action, params[:vm_name], '--force']
|
148
|
+
v_env.cli(args.compact)
|
149
|
+
{:id => params[:id]}
|
150
|
+
end
|
151
|
+
|
152
|
+
desc "Lists Vagrant environments"
|
153
|
+
get :list do
|
154
|
+
env_glob
|
155
|
+
end
|
156
|
+
|
157
|
+
desc "Halts a Vagrant environment"
|
158
|
+
params do
|
159
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
160
|
+
optional :force, :desc => 'Force shut down (equivalent of pulling power)'
|
161
|
+
optional :vm_name, :desc => 'single vm to act on'
|
162
|
+
end
|
163
|
+
post '/halt/:id' do
|
164
|
+
force = params[:force] == true ? '--force' : nil
|
165
|
+
args = [v_action, params[:vm_name], force]
|
166
|
+
v_env.cli(args.compact)
|
167
|
+
{:id => params[:id]}
|
168
|
+
end
|
169
|
+
|
170
|
+
desc "Initializes a Vagrant environment"
|
171
|
+
params do
|
172
|
+
optional :vagrantfile, :desc => "Vagrant environment configuration", :type => String
|
173
|
+
end
|
174
|
+
post :init do
|
175
|
+
machine = env_rand
|
176
|
+
machine_dir = File.join(env_dir, machine)
|
177
|
+
begin
|
178
|
+
Dir.mkdir(machine_dir, 0755)
|
179
|
+
rescue Errno::EEXIST
|
180
|
+
error!({ "error" => "#{machine_dir} already exists!" }, 409)
|
181
|
+
end
|
182
|
+
if params[:vagrantfile].nil?
|
183
|
+
v_env(machine).cli(v_action, box_name, box_url)
|
184
|
+
else
|
185
|
+
File.open(File.join(machine_dir, 'Vagrantfile'), 'w') {|f| f.write(params[:vagrantfile])}
|
186
|
+
end
|
187
|
+
{:id => machine}
|
188
|
+
end
|
189
|
+
|
190
|
+
desc "Provisions a Vagrant environment"
|
191
|
+
params do
|
192
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
193
|
+
optional :vm_name, :desc => 'single vm to act on'
|
194
|
+
end
|
195
|
+
post '/provision/:id' do
|
196
|
+
args = [v_action, params[:vm_name]]
|
197
|
+
v_env.cli(args.compact)
|
198
|
+
{:id => params[:id]}
|
199
|
+
end
|
200
|
+
|
201
|
+
desc "Initialize and provision an environment, returns the environment id"
|
202
|
+
params do
|
203
|
+
requires :box_name, :desc => 'Name for box, used to lookup already loaded box', :type => String, :regexp => /^[\w_-]+$/
|
204
|
+
optional :box_url, :desc => 'URL for box location, optional iff \'box_name\' exists', :type => String
|
205
|
+
optional :user_data_script, :desc => 'Script to invoke upon provisioning'
|
206
|
+
end
|
207
|
+
post :create do
|
208
|
+
machine_id = env_rand
|
209
|
+
machine_dir = make_machine_dir(machine_id)
|
210
|
+
addresses = v_file machine_id, machine_dir
|
211
|
+
args = 'up', '--provision'
|
212
|
+
add_task(:id => machine_id, :args => args)
|
213
|
+
{:id => machine_id, :ips => addresses}
|
214
|
+
end
|
215
|
+
|
216
|
+
desc "Initializes a Vagrant environment"
|
217
|
+
params do
|
218
|
+
optional :vagrantfile, :desc => "Vagrant environment configuration", :type => String
|
219
|
+
end
|
220
|
+
post :init do
|
221
|
+
machine_id = env_rand
|
222
|
+
machine_dir = make_machine_dir(machine_id)
|
223
|
+
if params[:vagrantfile].nil?
|
224
|
+
v_env(machine_id).cli(v_action, box_name, box_url)
|
225
|
+
else
|
226
|
+
v_file(machine_id, machine_dir, params[:vagrantfile])
|
227
|
+
end
|
228
|
+
{:id => machine_id}
|
229
|
+
end
|
230
|
+
|
231
|
+
desc "Purges a Vagrant environment"
|
232
|
+
params do
|
233
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
234
|
+
end
|
235
|
+
post '/purge/:id' do
|
236
|
+
if v_env.vms.all? {|vm| vm.last.state == 'not_created'}
|
237
|
+
machine_dir = File.join(env_dir, params[:id])
|
238
|
+
FileUtils.remove_entry_secure(machine_dir)
|
239
|
+
else
|
240
|
+
error!({ "error" => "Environment contains undestroyed machines!" }, 409)
|
241
|
+
end
|
242
|
+
{:id => params[:id]}
|
243
|
+
end
|
244
|
+
|
245
|
+
desc "Reloads a Vagrant environment"
|
246
|
+
params do
|
247
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
248
|
+
optional :no_provision, :desc => 'disable provisioning'
|
249
|
+
optional :vm_name, :desc => 'single vm to act on'
|
250
|
+
end
|
251
|
+
post '/reload/:id' do
|
252
|
+
provision = params[:no_provision] == true ? '--no-provision' : '--provision'
|
253
|
+
args = [v_action, params[:vm_name], provision]
|
254
|
+
v_env.cli(args.compact)
|
255
|
+
{:id => params[:id]}
|
256
|
+
end
|
257
|
+
|
258
|
+
desc "Resumes a Vagrant environment"
|
259
|
+
params do
|
260
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
261
|
+
optional :vm_name, :desc => 'single vm to act on'
|
262
|
+
end
|
263
|
+
post '/resume/:id' do
|
264
|
+
args = [v_action, params[:vm_name]]
|
265
|
+
v_env.cli(args.compact)
|
266
|
+
{:id => params[:id]}
|
267
|
+
end
|
268
|
+
|
269
|
+
desc "Prints the status of a Vagrant environment"
|
270
|
+
params do
|
271
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
272
|
+
end
|
273
|
+
get '/status/:id' do
|
274
|
+
state = {}
|
275
|
+
v_env.vms.each do |vm|
|
276
|
+
state[vm.first] = vm.last.state
|
277
|
+
end
|
278
|
+
{:status => state}
|
279
|
+
end
|
280
|
+
|
281
|
+
desc "Suspends a Vagrant environment"
|
282
|
+
params do
|
283
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
284
|
+
optional :vm_name, :desc => 'single vm to act on'
|
285
|
+
end
|
286
|
+
post '/suspend/:id' do
|
287
|
+
args = [v_action, params[:vm_name]]
|
288
|
+
v_env.cli(args.compact)
|
289
|
+
{:id => params[:id]}
|
290
|
+
end
|
291
|
+
|
292
|
+
desc "Boots a Vagrant environment"
|
293
|
+
params do
|
294
|
+
requires :id, :desc => "Vagrant environment id", :type => String, regexp: ENV_REGEX
|
295
|
+
optional :no_provision, :desc => 'disable provisioning'
|
296
|
+
optional :vm_name, :desc => 'single vm to act on'
|
297
|
+
end
|
298
|
+
post '/up/:id' do
|
299
|
+
provision = params[:no_provision] == true ? '--no-provision' : '--provision'
|
300
|
+
args = [v_action, params[:vm_name], provision]
|
301
|
+
v_env.cli(args.compact)
|
302
|
+
{:id => params[:id]}
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
resource :vms do
|
308
|
+
|
309
|
+
desc "Lists registered virtual machines"
|
310
|
+
get :registered do
|
311
|
+
out = %x{VBoxManage list vms}
|
312
|
+
{:vms => out.split('\n')}
|
313
|
+
end
|
314
|
+
|
315
|
+
desc "Lists running virtual machines"
|
316
|
+
get :running do
|
317
|
+
out = %x{VBoxManage list runningvms}
|
318
|
+
{:vms => out.split('\n')}
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fragrant do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
Fragrant::Frontend
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "GET /environments/list" do
|
11
|
+
it "returns an array of Vagrant environments" do
|
12
|
+
get "/environments/list"
|
13
|
+
last_response.status.should == 200
|
14
|
+
JSON.parse(last_response.body).should be_kind_of(Array)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
describe Fragrant::VagrantfileGenerator do
|
6
|
+
before do
|
7
|
+
@original_pwd = Dir.pwd
|
8
|
+
@working_directory = Dir.mktmpdir('fragrant-')
|
9
|
+
Dir.chdir @working_directory
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
Dir.chdir @original_pwd
|
14
|
+
FileUtils.rm_rf @working_directory
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should create a Vagrantfile' do
|
18
|
+
v = Fragrant::VagrantfileGenerator.new(Dir.pwd)
|
19
|
+
v.write
|
20
|
+
File.exist?(File.join(Dir.pwd, 'Vagrantfile')).should be_true
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'with a provisioning script passed as an option' do
|
24
|
+
before do
|
25
|
+
v = Fragrant::VagrantfileGenerator.new(Dir.pwd)
|
26
|
+
@custom_script = "#!/not/really/bin/bash\nDo this thing!"
|
27
|
+
v.add_script @custom_script
|
28
|
+
v.write
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should write the provisioning script as a separate file' do
|
32
|
+
File.read(File.join(Dir.pwd, 'script001')).should == @custom_script
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should refer to the provisioning script in the Vagrantfile' do
|
36
|
+
vagrantfile = File.read(File.join(Dir.pwd, 'Vagrantfile'))
|
37
|
+
vagrantfile.split("\n").grep(/provision.*script001/).should_not be_empty
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fragrant do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
Fragrant::Frontend
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "GET /vms/registered" do
|
11
|
+
it "returns an array of registered vms" do
|
12
|
+
get "/vms/registered"
|
13
|
+
last_response.status.should == 200
|
14
|
+
JSON.parse(last_response.body).should be_kind_of(Array)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "GET /vms/running" do
|
19
|
+
it "returns an array of running vms" do
|
20
|
+
get "/vms/running"
|
21
|
+
last_response.status.should == 200
|
22
|
+
JSON.parse(last_response.body).should be_kind_of(Array)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..'))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.setup :default, :test
|
6
|
+
require 'fragrant'
|
7
|
+
require 'rack/test'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.include Rack::Test::Methods
|
12
|
+
config.before do
|
13
|
+
FileUtils.mkdir_p(Fragrant.env_dir)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Michael Bleigh and Intridea, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|