@@ -2,27 +2,45 @@ class Cakebox
22 def Cakebox . configure ( config , user_settings )
33 require 'vagrant/util/deep_merge'
44 require 'json'
5+ require 'tempfile'
6+
7+ # Use absolute paths to construct file paths to support Vagrant Lookup Path
8+ # tree climbing (and thus running vagrant commands in any subfolder).
9+ currentFolder = "#{ File . dirname ( __FILE__ ) } "
10+ rootFolder = File . expand_path ( ".." , currentFolder )
511
612 # Define absolutely required box settings
713 settings = Hash . new
814 settings [ "vm" ] = Hash . new
915 settings [ "vm" ] [ "hostname" ] = "cakebox"
1016 settings [ "vm" ] [ "ip" ] = "10.33.10.10"
11- settings [ "vm" ] [ "memory" ] = 2048
17+ settings [ "vm" ] [ "memory" ] = 1024
1218 settings [ "vm" ] [ "cpus" ] = 1
1319 settings [ "cakebox" ] = Hash . new
14- settings [ "cakebox" ] [ "branch" ] = "master"
20+ settings [ "cakebox" ] [ "version" ] = "dev-master"
21+
22+ if user_settings == false
23+ user_settings = Hash . new
24+ end
1525
16- # Prevent merging empty vm user settings
17- user_settings [ "vm" ] = Hash . new if user_settings [ "vm" ] . nil?
18- user_settings [ "vm" ] [ "hostname" ] = settings [ "vm" ] [ "hostname" ] if user_settings [ "vm" ] [ "hostname" ] . nil?
19- user_settings [ "vm" ] [ "ip" ] = settings [ "vm" ] [ "ip" ] if user_settings [ "vm" ] [ "ip" ] . nil?
20- user_settings [ "vm" ] [ "memory" ] = settings [ "vm" ] [ "memory" ] if user_settings [ "vm" ] [ "memory" ] . nil?
21- user_settings [ "vm" ] [ "cpus" ] = settings [ "vm" ] [ "cpus" ] if user_settings [ "vm" ] [ "cpus" ] . nil?
22- user_settings [ "cakebox" ] [ "branch" ] = settings [ "cakebox" ] [ "branch" ] if user_settings [ "cakebox" ] [ "branch" ] . nil?
26+ # Deep merge user settings found in Cakebox.yaml. Uses the Vagrant Util
27+ # class to prevent a Vagrant plugin dependency + our custom 'compact' Hash
28+ # cleaner class to prevent non-DRY checking per setting.
29+ settings = Vagrant ::Util ::DeepMerge . deep_merge ( settings , user_settings . compact! )
30+ settings . tildeConvert!
31+
32+ # Determine Cakebox Dashboard protocol only once
33+ if settings [ 'cakebox' ] [ 'https' ] == true
34+ settings [ 'cakebox' ] [ 'protocol' ] = 'https'
35+ else
36+ settings [ 'cakebox' ] [ 'protocol' ] = 'http'
37+ end
2338
24- # Deep merge user settings found in Cakebox.yaml without plugin dependency
25- settings = Vagrant ::Util ::DeepMerge . deep_merge ( settings , user_settings )
39+ # Specify Vagrant post-up message
40+ config . vm . post_up_message =
41+ "Your box is ready and waiting.\n \n " +
42+ "=> Login to your Dashboard by browsing to " + settings [ 'cakebox' ] [ 'protocol' ] + '://' + settings [ "vm" ] [ "ip" ] + "\n " +
43+ "=> Login to your virtual machine by running: vagrant ssh"
2644
2745 # Specify CDN base-box and hostname for the vm
2846 config . vm . box = "cakebox"
@@ -41,9 +59,38 @@ def Cakebox.configure(config, user_settings)
4159 #vb.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"]
4260 end
4361
44- # Mount small (and thus fast) scripts folder instead of complete box root folder.
62+ # SSH copy bash aliases file to the box
63+ config . vm . provision "file" , source : currentFolder + File ::SEPARATOR + "aliases" , destination : "/home/vagrant/.bash_aliases"
64+
65+ # SSH copy local Cakebox.yaml to /home/vagrant/.cakebox when --provision is
66+ # being used so it can be used for virtual machine information.
67+ config . vm . provision "file" , source : rootFolder + File ::SEPARATOR + "Cakebox.yaml" , destination : "/home/vagrant/.cakebox/last-known-cakebox-yaml" #@todo
68+
69+ # SSH copy most recent local Git commit for alt3/cakebox to /home/vagrant/.cakebox
70+ composerVersionParts = settings [ 'cakebox' ] [ 'version' ] . split ( '-' )
71+ if composerVersionParts [ 1 ] . nil?
72+ raise Vagrant ::Errors ::VagrantError . new , 'Fatal: unable to extract local git branch from composer version "' + settings [ 'cakebox' ] [ 'version' ] + '"'
73+ end
74+ headFile = rootFolder + File ::SEPARATOR + ".git" + File ::SEPARATOR + "refs" + File ::SEPARATOR + "heads" + File ::SEPARATOR + "dev"
75+ config . vm . provision "file" , source : headFile , destination : "/home/vagrant/.cakebox/last-known-cakebox-commit"
76+
77+ # Write vagrant box version to file before ssh copying to /home/vagrant/.cakebox
78+ tempfile = Tempfile . new ( 'last-known-box-version' )
79+ boxes = `vagrant box list`
80+ boxes . match ( /cakebox\s +\( virtualbox,\s (.+)\) / )
81+ tempfile . write ( $1)
82+ config . vm . provision "file" , source : tempfile , destination : "/home/vagrant/.cakebox/last-known-box-version"
83+ tempfile . close
84+
85+ # Mount small (and thus fast) scripts folder instead of complete box root folder
4586 config . vm . synced_folder '.' , '/vagrant' , disabled : true
46- config . vm . synced_folder '.cakebox' , '/cakebox'
87+ config . vm . synced_folder '.cakebox' , '/cakebox' , :mount_options => [ "dmode=777" , "fmode=766" ] , create : true
88+
89+ # Temporarily mount .vagrant directory so we can replace the Vagrant 1.7.x
90+ # secure private key until these issues are resolved:
91+ # https://github.com/mitchellh/vagrant/issues/5090
92+ # https://github.com/mitchellh/vagrant/issues/4967
93+ config . vm . synced_folder '.vagrant' , '/vagrant' , :mount_options => [ "dmode=777" , "fmode=766" ] , create : true
4794
4895 # Create Vagrant Synced Folders for all yaml specified "folders".
4996 unless settings [ "synced_folders" ] . nil?
@@ -78,42 +125,46 @@ def Cakebox.configure(config, user_settings)
78125 # Replace insecure Vagrant ssh public key with user generated public key
79126 unless settings [ "security" ] . nil?
80127 unless settings [ "security" ] [ "box_public_key" ] . nil?
128+
81129 public_key = settings [ "security" ] [ "box_public_key" ]
82130 unless File . exists? ( public_key )
83- raise Vagrant ::Errors ::VagrantError . new , "Fatal: your public ssh key does not exist (#{ settings [ "security" ] [ "box_public_key" ] } )"
131+ raise Vagrant ::Errors ::VagrantError . new , "Fatal: your public SSH key does not exist (#{ public_key } )"
84132 end
85133
86- # A public key MUST be an accompanied by a private key
134+ # A public key MUST be accompanied by a private key
87135 if settings [ "security" ] [ "box_private_key" ] . nil?
88136 raise Vagrant ::Errors ::VagrantError . new , "Fatal: using a public ssh key also requires specifying a local private ssh key in your Cakebox.yaml"
89137 end
90- unless File . exists? ( settings [ "security" ] [ "box_private_key" ] )
91- raise Vagrant ::Errors ::VagrantError . new , "Fatal: your private ssh key does not exist (#{ settings [ "security" ] [ "box_private_key" ] } )"
138+
139+ private_key = settings [ "security" ] [ "box_private_key" ]
140+ unless File . exists? ( private_key )
141+ raise Vagrant ::Errors ::VagrantError . new , "Fatal: your private ssh key does not exist (#{ private_key } )"
92142 end
93143
94144 # Copy user's public key to the vm so it can be validated and applied
95- config . vm . provision "file" , source : settings [ "security" ] [ "box_public_key" ] , destination : "/home/vagrant/.ssh/" + File . basename ( settings [ "security" ] [ "box_public_key" ] )
145+ config . vm . provision "file" , source : public_key , destination : "/home/vagrant/.ssh/" + File . basename ( public_key )
96146
97147 # Add user's private key to all Vagrant-usable local private keys so all
98148 # required login scenarios will keep functioning as expected:
99149 # - initial non-secure vagrant up
100150 # - users protecting their box with a personally generated public key
101151 config . ssh . private_key_path = [
102- '~/.vagrant.d/insecure_private_key' ,
103- settings [ "security" ] [ "box_private_key" ]
152+ private_key ,
153+ Dir . home + '/.vagrant.d/insecure_private_key'
104154 ]
105155
106156 # Run bash script to replace insecure public key in authorized_keys
107157 config . vm . provision "shell" do |s |
108- s . inline = "bash /cakebox/bash/replace-insecure-key .sh $@"
109- s . args = "/home/vagrant/.ssh/" + File . basename ( settings [ "security" ] [ "box_public_key" ] )
158+ s . inline = "bash /cakebox/bash/ssh-authentication .sh $@"
159+ s . args = [ File . basename ( public_key ) , File . basename ( private_key ) ]
110160 end
161+
162+ # Prevent Vagrant 1.7.x from generating a new private key and inserting
163+ # corresponding public key (overwriting our just set custom key).
164+ config . ssh . insert_key = false
111165 end
112166 end
113167
114- # SSH copy bash aliases file to the box
115- config . vm . provision "file" , source : ".cakebox/aliases" , destination : "/home/vagrant/.bash_aliases"
116-
117168 # Always display SSH Agent Forwarding sanity checks
118169 config . vm . provision "shell" do |s |
119170 s . privileged = false
@@ -124,7 +175,31 @@ def Cakebox.configure(config, user_settings)
124175 config . vm . provision "shell" do |s |
125176 s . privileged = false
126177 s . inline = "bash /cakebox/bash/console-installer.sh $@"
127- s . args = user_settings [ "cakebox" ] [ "branch" ]
178+ s . args = settings [ "cakebox" ] [ "version" ]
179+ end
180+
181+ # Run cakebox self-update
182+ config . vm . provision "shell" do |s |
183+ s . privileged = false
184+ s . inline = "bash /cakebox/console/bin/cake update self"
185+ end
186+
187+ # Set Cakebox Dashboard protocol to HTTP or HTTPS
188+ config . vm . provision "shell" do |s |
189+ s . privileged = false
190+ s . inline = "bash /cakebox/console/bin/cake config dashboard --force --protocol $@"
191+ s . args = settings [ "cakebox" ] [ "protocol" ]
192+ end
193+
194+ # Turn CakePHP debug mode on/off for Cakebox Commands and Dashboard
195+ config . vm . provision "shell" do |s |
196+ s . privileged = false
197+ s . inline = "bash /cakebox/console/bin/cake config debug $@"
198+ if settings [ "cakebox" ] [ "debug" ] == false
199+ s . args = 'off'
200+ else
201+ s . args = 'on'
202+ end
128203 end
129204
130205 # Set global git username and email using `cakebox config git [options]`
@@ -143,14 +218,14 @@ def Cakebox.configure(config, user_settings)
143218 end
144219 end
145220
146- # Create Nginx site configuration files for all yaml specified "sites "
147- unless settings [ "sites " ] . nil?
148- settings [ "sites " ] . each do |site |
221+ # Create Nginx virtual hosts for all yaml specified "vhosts "
222+ unless settings [ "vhosts " ] . nil?
223+ settings [ "vhosts " ] . each do |vhost |
149224 config . vm . provision "shell" do |s |
150225 s . privileged = false
151- s . inline = "bash /cakebox/console/bin/cake site add $@"
152- s . args = [ site [ "url" ] , site [ "webroot" ] ]
153- s . args . push ( site [ "options" ] ) if !site [ "options" ] . nil?
226+ s . inline = "bash /cakebox/console/bin/cake vhost add $@"
227+ s . args = [ vhost [ "url" ] , vhost [ "webroot" ] ]
228+ s . args . push ( vhost [ "options" ] ) if !vhost [ "options" ] . nil?
154229 end
155230 end
156231 end
@@ -167,34 +242,123 @@ def Cakebox.configure(config, user_settings)
167242 end
168243 end
169244
170- # Install fully working framework applications for all yaml specified "apps"
245+ # Install fully working framework applications for all yaml specified "apps".
246+ # The --repair parameter is appended so only missing components will be
247+ # installed when the sources already exist (e.g. when recreating a new box
248+ # with existing sources in a mapped Synced Folder)
171249 unless settings [ "apps" ] . nil?
172250 settings [ "apps" ] . each do |app |
173251 config . vm . provision "shell" do |s |
174252 s . privileged = false
175253 s . inline = "bash /cakebox/console/bin/cake application add $@"
176254 s . args = [ app [ "url" ] ]
177255 s . args . push ( app [ "options" ] ) if !app [ "options" ] . nil?
256+ s . args . push ( '--repair' )
178257 end
179258 end
180259 end
181260
182- # Install all yaml specified "additional_software" packages
183- unless settings [ "software" ] . nil?
184- settings [ "software" ] . each do |package |
185- config . vm . provision "shell" do |s |
186- s . privileged = false
187- s . inline = "bash /cakebox/console/bin/cake package add $@"
188- s . args = [ package [ "package" ] ]
261+ # Install extras
262+ unless settings [ "extra" ] . nil?
263+ settings [ "extra" ] . each do | hash |
264+ hashKey = hash . keys . first
265+ unless hash [ hashKey ] . nil?
266+
267+ # Install additional apt packages from the Ubuntu Package Archive
268+ if hashKey == "apt_packages"
269+ hash [ hashKey ] . each do | package |
270+ config . vm . provision "shell" do |s |
271+ s . privileged = false
272+ s . inline = "bash /cakebox/console/bin/cake package add $@"
273+ s . args = [ package ]
274+ end
275+ end
276+ end
277+
278+ # Upload and run user created bash scripts
279+ if hashKey == "scripts"
280+ hash [ hashKey ] . each do | script |
281+ remoteCopy = "/home/vagrant/.cakebox/last-known-script." + File . basename ( script . to_s )
282+ config . vm . provision "file" , source : script , destination : remoteCopy
283+ config . vm . provision "shell" do |s |
284+ s . privileged = false
285+ s . inline = "bash " + remoteCopy
286+ end
287+ end
288+ end
189289 end
190290 end
191291 end
192292
193- # Provide user with box-info
194- config . vm . provision "shell" do |s |
195- s . inline = "bash /cakebox/bash/box-info.sh $@"
196- s . args = [ settings [ "vm" ] [ "ip" ] ]
293+ end
294+ end
295+
296+
297+
298+ # Hash cleaner, removes nil/empty values recursively from a hash.
299+ #
300+ # Very handy to avoid errors when user yaml file does not include all required parts
301+ # After removing nil values, it can safely be deep merged into default settings,
302+ # without the need to check for all keys being present or having a value
303+ class Hash
304+ def compact!
305+ self . delete_if do |key , val |
306+
307+ if block_given?
308+ yield ( key , val )
309+ else
310+ test1 = val . nil?
311+ test2 = val . empty? if val . respond_to? ( 'empty?' )
312+ test3 = val . strip . empty? if val . is_a? ( String ) && val . respond_to? ( 'empty?' )
313+
314+ test1 || test2 || test3
315+ end
316+ end
317+
318+ self . each do |key , val |
319+ if self [ key ] . is_a? ( Hash ) && self [ key ] . respond_to? ( 'compact!' )
320+ if block_given?
321+ self [ key ] = self [ key ] . compact! ( &Proc . new )
322+ else
323+ self [ key ] = self [ key ] . compact!
324+ end
325+ end
197326 end
198327
328+ return self
329+ end
330+ end
331+
332+ # Recursively searches a Hash for Strings starting with ~ and then replaces ~
333+ # with OS independent Dir.home so we can support the use of ~ on Windows too.
334+ class Hash
335+ def tildeConvert!
336+ self . each do | key , value |
337+
338+ if value . is_a? ( String )
339+ if ( value =~ /^~/ )
340+ self [ key ] = value . sub ( /^~/ , Dir . home )
341+ end
342+ end
343+
344+ if value . is_a? ( Array )
345+ value . each_with_index do | arrayElement , i |
346+ if arrayElement . is_a? ( String )
347+ if ( arrayElement =~ /^~/ )
348+ self [ key ] [ i ] = arrayElement . sub ( /^~/ , Dir . home )
349+ end
350+ end
351+
352+ if arrayElement . is_a? ( Hash )
353+ arrayElement . tildeConvert!
354+ end
355+ end
356+ end
357+
358+ if value . is_a? ( Hash )
359+ value . tildeConvert!
360+ end
361+
362+ end
199363 end
200364end
0 commit comments