vagrant 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/Gemfile +4 -4
  2. data/Rakefile +3 -3
  3. data/VERSION +1 -1
  4. data/bin/vagrant +5 -13
  5. data/config/default.rb +1 -0
  6. data/keys/README.md +8 -1
  7. data/keys/vagrant.ppk +26 -0
  8. data/lib/vagrant.rb +3 -3
  9. data/lib/vagrant/actions/base.rb +15 -4
  10. data/lib/vagrant/actions/box/add.rb +1 -1
  11. data/lib/vagrant/actions/box/download.rb +72 -66
  12. data/lib/vagrant/actions/box/unpackage.rb +1 -4
  13. data/lib/vagrant/actions/runner.rb +1 -1
  14. data/lib/vagrant/actions/vm/boot.rb +5 -7
  15. data/lib/vagrant/actions/vm/customize.rb +2 -2
  16. data/lib/vagrant/actions/vm/destroy.rb +2 -2
  17. data/lib/vagrant/actions/vm/down.rb +7 -0
  18. data/lib/vagrant/actions/vm/export.rb +10 -4
  19. data/lib/vagrant/actions/vm/forward_ports.rb +5 -15
  20. data/lib/vagrant/actions/vm/halt.rb +5 -3
  21. data/lib/vagrant/actions/vm/import.rb +10 -3
  22. data/lib/vagrant/actions/vm/move_hard_drive.rb +1 -3
  23. data/lib/vagrant/actions/vm/package.rb +33 -10
  24. data/lib/vagrant/actions/vm/provision.rb +4 -4
  25. data/lib/vagrant/actions/vm/reload.rb +1 -1
  26. data/lib/vagrant/actions/vm/resume.rb +1 -1
  27. data/lib/vagrant/actions/vm/shared_folders.rb +7 -7
  28. data/lib/vagrant/actions/vm/start.rb +3 -2
  29. data/lib/vagrant/actions/vm/suspend.rb +2 -2
  30. data/lib/vagrant/actions/vm/up.rb +7 -17
  31. data/lib/vagrant/active_list.rb +52 -45
  32. data/lib/vagrant/box.rb +18 -11
  33. data/lib/vagrant/busy.rb +7 -0
  34. data/lib/vagrant/command.rb +27 -0
  35. data/lib/vagrant/commands/base.rb +163 -0
  36. data/lib/vagrant/commands/box.rb +16 -0
  37. data/lib/vagrant/commands/box/add.rb +24 -0
  38. data/lib/vagrant/commands/box/list.rb +30 -0
  39. data/lib/vagrant/commands/box/remove.rb +31 -0
  40. data/lib/vagrant/commands/destroy.rb +23 -0
  41. data/lib/vagrant/commands/down.rb +16 -0
  42. data/lib/vagrant/commands/halt.rb +23 -0
  43. data/lib/vagrant/commands/init.rb +32 -0
  44. data/lib/vagrant/commands/package.rb +46 -0
  45. data/lib/vagrant/commands/reload.rb +22 -0
  46. data/lib/vagrant/commands/resume.rb +22 -0
  47. data/lib/vagrant/commands/ssh.rb +22 -0
  48. data/lib/vagrant/commands/ssh_config.rb +30 -0
  49. data/lib/vagrant/commands/status.rb +58 -0
  50. data/lib/vagrant/commands/suspend.rb +23 -0
  51. data/lib/vagrant/commands/up.rb +26 -0
  52. data/lib/vagrant/config.rb +21 -11
  53. data/lib/vagrant/downloaders/file.rb +5 -5
  54. data/lib/vagrant/downloaders/http.rb +10 -15
  55. data/lib/vagrant/environment.rb +259 -0
  56. data/lib/vagrant/provisioners/base.rb +7 -0
  57. data/lib/vagrant/provisioners/chef.rb +24 -9
  58. data/lib/vagrant/provisioners/chef_server.rb +23 -48
  59. data/lib/vagrant/provisioners/chef_solo.rb +48 -22
  60. data/lib/vagrant/ssh.rb +95 -46
  61. data/lib/vagrant/util.rb +2 -2
  62. data/lib/vagrant/util/errors.rb +36 -0
  63. data/lib/vagrant/util/platform.rb +12 -0
  64. data/lib/vagrant/util/progress_meter.rb +33 -0
  65. data/lib/vagrant/util/stacked_proc_runner.rb +35 -0
  66. data/lib/vagrant/util/template_renderer.rb +83 -0
  67. data/lib/vagrant/vm.rb +1 -0
  68. data/templates/{Vagrantfile → Vagrantfile.erb} +2 -2
  69. data/templates/chef_server_client.erb +16 -0
  70. data/templates/chef_solo_solo.erb +4 -0
  71. data/templates/errors.yml +157 -0
  72. data/templates/package_Vagrantfile.erb +11 -0
  73. data/templates/ssh_config.erb +7 -0
  74. data/test/test_helper.rb +12 -15
  75. data/test/vagrant/actions/box/add_test.rb +1 -2
  76. data/test/vagrant/actions/box/destroy_test.rb +0 -1
  77. data/test/vagrant/actions/box/download_test.rb +40 -15
  78. data/test/vagrant/actions/box/unpackage_test.rb +2 -3
  79. data/test/vagrant/actions/collection_test.rb +8 -5
  80. data/test/vagrant/actions/runner_test.rb +8 -6
  81. data/test/vagrant/actions/vm/boot_test.rb +12 -11
  82. data/test/vagrant/actions/vm/customize_test.rb +2 -3
  83. data/test/vagrant/actions/vm/destroy_test.rb +2 -3
  84. data/test/vagrant/actions/vm/down_test.rb +16 -3
  85. data/test/vagrant/actions/vm/export_test.rb +4 -5
  86. data/test/vagrant/actions/vm/forward_ports_test.rb +6 -5
  87. data/test/vagrant/actions/vm/halt_test.rb +8 -2
  88. data/test/vagrant/actions/vm/import_test.rb +5 -5
  89. data/test/vagrant/actions/vm/move_hard_drive_test.rb +4 -6
  90. data/test/vagrant/actions/vm/package_test.rb +60 -22
  91. data/test/vagrant/actions/vm/provision_test.rb +7 -16
  92. data/test/vagrant/actions/vm/reload_test.rb +3 -2
  93. data/test/vagrant/actions/vm/resume_test.rb +0 -1
  94. data/test/vagrant/actions/vm/shared_folders_test.rb +17 -12
  95. data/test/vagrant/actions/vm/start_test.rb +10 -3
  96. data/test/vagrant/actions/vm/suspend_test.rb +1 -2
  97. data/test/vagrant/actions/vm/up_test.rb +19 -11
  98. data/test/vagrant/active_list_test.rb +148 -129
  99. data/test/vagrant/box_test.rb +26 -14
  100. data/test/vagrant/busy_test.rb +15 -6
  101. data/test/vagrant/command_test.rb +53 -0
  102. data/test/vagrant/commands/base_test.rb +118 -0
  103. data/test/vagrant/commands/box/add_test.rb +34 -0
  104. data/test/vagrant/commands/box/list_test.rb +32 -0
  105. data/test/vagrant/commands/box/remove_test.rb +41 -0
  106. data/test/vagrant/commands/destroy_test.rb +32 -0
  107. data/test/vagrant/commands/down_test.rb +17 -0
  108. data/test/vagrant/commands/halt_test.rb +28 -0
  109. data/test/vagrant/commands/init_test.rb +55 -0
  110. data/test/vagrant/commands/package_test.rb +84 -0
  111. data/test/vagrant/commands/reload_test.rb +28 -0
  112. data/test/vagrant/commands/resume_test.rb +33 -0
  113. data/test/vagrant/commands/ssh_config_test.rb +54 -0
  114. data/test/vagrant/commands/ssh_test.rb +32 -0
  115. data/test/vagrant/commands/status_test.rb +20 -0
  116. data/test/vagrant/commands/suspend_test.rb +33 -0
  117. data/test/vagrant/commands/up_test.rb +41 -0
  118. data/test/vagrant/config_test.rb +42 -17
  119. data/test/vagrant/downloaders/file_test.rb +7 -0
  120. data/test/vagrant/downloaders/http_test.rb +12 -0
  121. data/test/vagrant/environment_test.rb +595 -0
  122. data/test/vagrant/provisioners/base_test.rb +7 -1
  123. data/test/vagrant/provisioners/chef_server_test.rb +41 -51
  124. data/test/vagrant/provisioners/chef_solo_test.rb +93 -62
  125. data/test/vagrant/provisioners/chef_test.rb +61 -15
  126. data/test/vagrant/ssh_test.rb +166 -38
  127. data/test/vagrant/util/errors_test.rb +57 -0
  128. data/test/vagrant/util/progress_meter_test.rb +33 -0
  129. data/test/vagrant/{stacked_proc_runner_test.rb → util/stacked_proc_runner_test.rb} +3 -3
  130. data/test/vagrant/util/template_renderer_test.rb +138 -0
  131. data/test/vagrant/vm_test.rb +3 -2
  132. data/vagrant.gemspec +88 -33
  133. metadata +94 -51
  134. data/bin/vagrant-box +0 -34
  135. data/bin/vagrant-down +0 -27
  136. data/bin/vagrant-halt +0 -28
  137. data/bin/vagrant-init +0 -27
  138. data/bin/vagrant-package +0 -29
  139. data/bin/vagrant-reload +0 -29
  140. data/bin/vagrant-resume +0 -27
  141. data/bin/vagrant-ssh +0 -27
  142. data/bin/vagrant-status +0 -29
  143. data/bin/vagrant-suspend +0 -27
  144. data/bin/vagrant-up +0 -29
  145. data/lib/vagrant/commands.rb +0 -234
  146. data/lib/vagrant/env.rb +0 -189
  147. data/lib/vagrant/stacked_proc_runner.rb +0 -33
  148. data/test/vagrant/commands_test.rb +0 -269
  149. data/test/vagrant/env_test.rb +0 -418
@@ -4,6 +4,7 @@ module Vagrant
4
4
  class ChefSolo < Chef
5
5
  def prepare
6
6
  share_cookbook_folders
7
+ share_role_folders
7
8
  end
8
9
 
9
10
  def provision!
@@ -15,24 +16,28 @@ module Vagrant
15
16
 
16
17
  def share_cookbook_folders
17
18
  host_cookbook_paths.each_with_index do |cookbook, i|
18
- Vagrant.config.vm.share_folder("vagrant-chef-solo-#{i}", cookbook_path(i), cookbook)
19
+ env.config.vm.share_folder("v-csc-#{i}", cookbook_path(i), cookbook)
19
20
  end
20
21
  end
21
22
 
22
- def setup_solo_config
23
- solo_file = <<-solo
24
- file_cache_path "#{Vagrant.config.chef.provisioning_path}"
25
- cookbook_path #{cookbooks_path}
26
- solo
23
+ def share_role_folders
24
+ host_role_paths.each_with_index do |role, i|
25
+ env.config.vm.share_folder("v-csr-#{i}", role_path(i), role)
26
+ end
27
+ end
27
28
 
28
- logger.info "Uploading chef-solo configuration script..."
29
- SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "solo.rb"))
29
+ def setup_solo_config
30
+ setup_config("chef_solo_solo", "solo.rb", {
31
+ :provisioning_path => env.config.chef.provisioning_path,
32
+ :cookbooks_path => cookbooks_path,
33
+ :roles_path => roles_path
34
+ })
30
35
  end
31
36
 
32
37
  def run_chef_solo
33
38
  logger.info "Running chef-solo..."
34
- SSH.execute do |ssh|
35
- ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream|
39
+ env.ssh.execute do |ssh|
40
+ ssh.exec!("cd #{env.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream|
36
41
  # TODO: Very verbose. It would be easier to save the data and only show it during
37
42
  # an error, or when verbosity level is set high
38
43
  logger.info("#{stream}: #{data}")
@@ -40,28 +45,49 @@ solo
40
45
  end
41
46
  end
42
47
 
43
- def host_cookbook_paths
44
- cookbooks = Vagrant.config.chef.cookbooks_path
45
- cookbooks = [cookbooks] unless cookbooks.is_a?(Array)
46
- cookbooks.collect! { |cookbook| File.expand_path(cookbook, Env.root_path) }
47
- return cookbooks
48
+ def host_folder_paths(paths)
49
+ [paths].flatten.collect { |path| File.expand_path(path, env.root_path) }
48
50
  end
49
51
 
50
- def cookbook_path(i)
51
- File.join(Vagrant.config.chef.provisioning_path, "cookbooks-#{i}")
52
+ def folder_path(folder, i)
53
+ File.join(env.config.chef.provisioning_path, "#{folder}-#{i}")
52
54
  end
53
55
 
54
- def cookbooks_path
56
+ def folders_path(folders, folder)
55
57
  result = []
56
- host_cookbook_paths.each_with_index do |host_path, i|
57
- result << cookbook_path(i)
58
+ folders.each_with_index do |host_path, i|
59
+ result << folder_path(folder, i)
58
60
  end
59
61
 
60
62
  # We're lucky that ruby's string and array syntax for strings is the
61
63
  # same as JSON, so we can just convert to JSON here and use that
62
- result = result.to_s if result.length == 1
64
+ result = result[0].to_s if result.length == 1
63
65
  result.to_json
64
66
  end
67
+
68
+ def host_cookbook_paths
69
+ host_folder_paths(env.config.chef.cookbooks_path)
70
+ end
71
+
72
+ def host_role_paths
73
+ host_folder_paths(env.config.chef.roles_path)
74
+ end
75
+
76
+ def cookbook_path(i)
77
+ folder_path("cookbooks", i)
78
+ end
79
+
80
+ def role_path(i)
81
+ folder_path("roles", i)
82
+ end
83
+
84
+ def cookbooks_path
85
+ folders_path(host_cookbook_paths, "cookbooks")
86
+ end
87
+
88
+ def roles_path
89
+ folders_path(host_role_paths, "roles")
90
+ end
65
91
  end
66
92
  end
67
- end
93
+ end
@@ -1,68 +1,117 @@
1
1
  module Vagrant
2
+ # Manages SSH access to a specific environment. Allows an environment to
3
+ # replace the process with SSH itself, run a specific set of commands,
4
+ # upload files, or even check if a host is up.
2
5
  class SSH
3
6
  include Vagrant::Util
4
7
 
5
- class << self
6
- def connect(opts={})
7
- options = {}
8
- [:host, :username, :private_key_path].each do |param|
9
- options[param] = opts[param] || Vagrant.config.ssh.send(param)
10
- end
8
+ # Reference back up to the environment which this SSH object belongs
9
+ # to
10
+ attr_accessor :env
11
+
12
+ def initialize(environment)
13
+ @env = environment
14
+ end
11
15
 
12
- Kernel.exec "ssh -p #{port(opts)} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{options[:private_key_path]} #{options[:username]}@#{options[:host]}".strip
16
+ # Connects to the environment's virtual machine, replacing the ruby
17
+ # process with an SSH process. This method optionally takes a hash
18
+ # of options which override the configuration values.
19
+ def connect(opts={})
20
+ if Mario::Platform.windows?
21
+ error_and_exit(:ssh_unavailable_windows,
22
+ :key_path => env.config.ssh.private_key_path,
23
+ :ssh_port => port(opts))
13
24
  end
14
25
 
15
- def execute(opts={})
16
- Net::SSH.start(Vagrant.config.ssh.host,
17
- Vagrant.config[:ssh][:username],
18
- opts.merge( :port => port,
19
- :keys => [Vagrant.config.ssh.private_key_path])) do |ssh|
20
- yield ssh
21
- end
26
+ options = {}
27
+ [:host, :username, :private_key_path].each do |param|
28
+ options[param] = opts[param] || env.config.ssh.send(param)
22
29
  end
23
30
 
24
- def upload!(from, to)
25
- execute do |ssh|
26
- scp = Net::SCP.new(ssh)
27
- scp.upload!(from, to)
28
- end
31
+ check_key_permissions(options[:private_key_path])
32
+
33
+ # Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
34
+ # (GH-51). As a workaround, we fork and wait. On all other platforms,
35
+ # we simply exec.
36
+ pid = nil
37
+ pid = fork if Util::Platform.leopard?
38
+ Kernel.exec "ssh -p #{port(opts)} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{options[:private_key_path]} #{options[:username]}@#{options[:host]}".strip if pid.nil?
39
+ Process.wait(pid) if pid
40
+ end
41
+
42
+ # Opens an SSH connection to this environment's virtual machine and yields
43
+ # a Net::SSH object which can be used to execute remote commands.
44
+ def execute(opts={})
45
+ Net::SSH.start(env.config.ssh.host,
46
+ env.config[:ssh][:username],
47
+ opts.merge( :port => port,
48
+ :keys => [env.config.ssh.private_key_path])) do |ssh|
49
+ yield ssh
50
+ end
51
+ end
52
+
53
+ # Uploads a file from `from` to `to`. `from` is expected to be a filename
54
+ # or StringIO, and `to` is expected to be a path. This method simply forwards
55
+ # the arguments to `Net::SCP#upload!` so view that for more information.
56
+ def upload!(from, to)
57
+ execute do |ssh|
58
+ scp = Net::SCP.new(ssh)
59
+ scp.upload!(from, to)
29
60
  end
61
+ end
30
62
 
31
- def up?
32
- check_thread = Thread.new do
33
- begin
34
- Thread.current[:result] = false
35
- execute(:timeout => Vagrant.config.ssh.timeout) do |ssh|
36
- Thread.current[:result] = true
37
- end
38
- rescue Errno::ECONNREFUSED, Net::SSH::Disconnect
39
- # False, its defaulted above
63
+ # Checks if this environment's machine is up (i.e. responding to SSH).
64
+ #
65
+ # @return [Boolean]
66
+ def up?
67
+ check_thread = Thread.new do
68
+ begin
69
+ Thread.current[:result] = false
70
+ execute(:timeout => env.config.ssh.timeout) do |ssh|
71
+ Thread.current[:result] = true
40
72
  end
73
+ rescue Errno::ECONNREFUSED, Net::SSH::Disconnect
74
+ # False, its defaulted above
41
75
  end
76
+ end
42
77
 
43
- check_thread.join(Vagrant.config.ssh.timeout)
44
- return check_thread[:result]
45
- rescue Net::SSH::AuthenticationFailed
46
- error_and_exit(<<-msg)
47
- SSH authentication failed! While this could be due to a variety of reasons,
48
- the two most common are: private key path is incorrect or you're using a box
49
- which was built for Vagrant 0.1.x.
78
+ check_thread.join(env.config.ssh.timeout)
79
+ return check_thread[:result]
80
+ rescue Net::SSH::AuthenticationFailed
81
+ error_and_exit(:vm_ssh_auth_failed)
82
+ end
50
83
 
51
- Vagrant 0.2.x dropped support for password-based authentication. If you're
52
- tring to `vagrant up` a box which does not support Vagrant's private/public
53
- keypair, then this error will be raised. To resolve this, read the guide
54
- on converting base boxes from password-based to keypairs here:
84
+ # Checks the file permissions for the private key, resetting them
85
+ # if needed, or on failure erroring.
86
+ def check_key_permissions(key_path)
87
+ # TODO: This only works on unix based systems for now. Windows
88
+ # systems will need to be investigated further.
89
+ stat = File.stat(key_path)
55
90
 
56
- http://vagrantup.com/docs/converting_password_to_key_ssh.html
91
+ if stat.owned? && file_perms(key_path) != "600"
92
+ logger.info "Permissions on private key incorrect, fixing..."
93
+ File.chmod(0600, key_path)
57
94
 
58
- If the box was built for 0.2.x and contains a custom public key, perhaps
59
- the path to the private key is incorrect. Check your `config.ssh.private_key_path`.
60
- msg
95
+ error_and_exit(:ssh_bad_permissions, :key_path => key_path) if file_perms(key_path) != "600"
61
96
  end
97
+ rescue Errno::EPERM
98
+ # This shouldn't happen since we verify we own the file, but just
99
+ # in case.
100
+ error_and_exit(:ssh_bad_permissions, :key_path => key_path)
101
+ end
62
102
 
63
- def port(opts={})
64
- opts[:port] || Vagrant.config.vm.forwarded_ports[Vagrant.config.ssh.forwarded_port_key][:hostport]
65
- end
103
+ # Returns the file permissions of a given file. This is fairly unix specific
104
+ # and probably doesn't belong in this class. Will be refactored out later.
105
+ def file_perms(path)
106
+ perms = sprintf("%o", File.stat(path).mode)
107
+ perms.reverse[0..2].reverse
108
+ end
109
+
110
+ # Returns the port which is either given in the options hash or taken from
111
+ # the config by finding it in the forwarded ports hash based on the
112
+ # `config.ssh.forwarded_port_key`
113
+ def port(opts={})
114
+ opts[:port] || env.config.vm.forwarded_ports[env.config.ssh.forwarded_port_key][:hostport]
66
115
  end
67
116
  end
68
117
  end
@@ -10,12 +10,12 @@ module Vagrant
10
10
  puts "====================================================================="
11
11
  end
12
12
 
13
- def error_and_exit(error)
13
+ def error_and_exit(key, data = {})
14
14
  abort <<-error
15
15
  =====================================================================
16
16
  Vagrant experienced an error!
17
17
 
18
- #{error.chomp}
18
+ #{Errors.error_string(key, data).chomp}
19
19
  =====================================================================
20
20
  error
21
21
  end
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ module Vagrant
4
+ module Util
5
+ # This class is responsible for outputting errors. It retrieves the errors,
6
+ # based on their key, from the error file, and then outputs it.
7
+ class Errors
8
+ @@errors = nil
9
+
10
+ class <<self
11
+ # Resets the internal errors hash to nil, forcing a reload on the next
12
+ # access of {errors}.
13
+ def reset!
14
+ @@errors = nil
15
+ end
16
+
17
+ # Returns the hash of errors from the error YML files. This only loads once,
18
+ # then returns a cached value until {reset!} is called.
19
+ #
20
+ # @return [Hash]
21
+ def errors
22
+ @@errors ||= YAML.load_file(File.join(PROJECT_ROOT, "templates", "errors.yml"))
23
+ end
24
+
25
+ # Renders the error with the given key and data parameters and returns
26
+ # the rendered result.
27
+ #
28
+ # @return [String]
29
+ def error_string(key, data = {})
30
+ template = errors[key] || "Unknown error key: #{key}"
31
+ TemplateRenderer.render_string(template, data)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ module Vagrant
2
+ module Util
3
+ # This class just contains some platform checking code.
4
+ class Platform
5
+ class <<self
6
+ def leopard?
7
+ RUBY_PLATFORM.downcase.include?("darwin9")
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ module Vagrant
2
+ module Util
3
+ # A mixin which allows any class to be able to show a "progress meter"
4
+ # to standard out. The progress meter shows the progress of an operation
5
+ # with console-animated text in stdout.
6
+ module ProgressMeter
7
+ # Updates the progress meter with the given progress amount and total.
8
+ # This method will do the math to figure out a percentage and show it
9
+ # within stdout.
10
+ #
11
+ # @param [Float] progress Progress
12
+ # @param [Float] total Total
13
+ def update_progress(progress, total, show_parts=true)
14
+ percent = (progress.to_f / total.to_f) * 100
15
+ print "#{cl_reset}Progress: #{percent.to_i}%"
16
+ print " (#{progress} / #{total})" if show_parts
17
+ $stdout.flush
18
+ end
19
+
20
+ # Completes the progress meter by resetting it off of the screen.
21
+ def complete_progress
22
+ # Just clear the line back out
23
+ print "#{cl_reset}"
24
+ end
25
+
26
+ def cl_reset
27
+ reset = "\r"
28
+ reset += "\e[0K" unless Mario::Platform.windows?
29
+ reset
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,35 @@
1
+ module Vagrant
2
+ module Util
3
+ # Represents the "stacked proc runner" behavior which is used a
4
+ # couple places within Vagrant. This allows procs to "stack" on
5
+ # each other, then all execute in a single action. An example of
6
+ # its uses can be seen in the {Config} class.
7
+ module StackedProcRunner
8
+ # Returns the proc stack. This should always be called as the
9
+ # accessor of the stack. The instance variable itself should _never_
10
+ # be used.
11
+ #
12
+ # @return [Array<Proc>]
13
+ def proc_stack
14
+ @_proc_stack ||= []
15
+ end
16
+
17
+ # Adds (pushes) a proc to the stack. The actual proc added here is
18
+ # not executed, but merely stored.
19
+ #
20
+ # @param [Proc] block
21
+ def push_proc(&block)
22
+ proc_stack << block
23
+ end
24
+
25
+ # Executes all the procs on the stack, passing in the given arguments.
26
+ # The stack is not cleared afterwords. It is up to the user of this
27
+ # mixin to clear the stack by calling `proc_stack.clear`.
28
+ def run_procs!(*args)
29
+ proc_stack.each do |proc|
30
+ proc.call(*args)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,83 @@
1
+ require 'ostruct'
2
+ require 'erb'
3
+
4
+ module Vagrant
5
+ module Util
6
+ # This class is used to render the ERB templates in the
7
+ # `GEM_ROOT/templates` directory.
8
+ class TemplateRenderer < OpenStruct
9
+ class <<self
10
+ # Render a given template and return the result. This method optionally
11
+ # takes a block which will be passed the renderer prior to rendering, which
12
+ # allows the caller to set any view variables within the renderer itself.
13
+ #
14
+ # @return [String] Rendered template
15
+ def render(*args)
16
+ render_with(:render, *args)
17
+ end
18
+
19
+ # Render a given string and return the result. This method optionally
20
+ # takes a block which will be passed the renderer prior to rendering, which
21
+ # allows the caller to set any view variables within the renderer itself.
22
+ #
23
+ # @param [String] template The template data string.
24
+ # @return [String] Rendered template
25
+ def render_string(*args)
26
+ render_with(:render_string, *args)
27
+ end
28
+
29
+ # Method used internally to DRY out the other renderers. This method
30
+ # creates and sets up the renderer before calling a specified method on it.
31
+ def render_with(method, template, data={})
32
+ renderer = new(template, data)
33
+ yield renderer if block_given?
34
+ renderer.send(method.to_sym)
35
+ end
36
+ end
37
+
38
+ def initialize(template, data = {})
39
+ super()
40
+
41
+ data[:template] = template
42
+ data.each do |key, value|
43
+ send("#{key}=", value)
44
+ end
45
+ end
46
+
47
+ # Renders the template using the class intance as the binding. Because the
48
+ # renderer inherits from `OpenStruct`, additional view variables can be
49
+ # added like normal accessors.
50
+ #
51
+ # @return [String]
52
+ def render
53
+ # TODO: Seems like a pretty dirty way to do this. Perhaps refactor this
54
+ old_template = template
55
+ result = nil
56
+ File.open(full_template_path, 'r') do |f|
57
+ self.template = f.read
58
+ result = render_string
59
+ end
60
+
61
+ result
62
+ ensure
63
+ self.template = old_template
64
+ end
65
+
66
+ # Renders a template, handling the template as a string, but otherwise
67
+ # acting the same way as {#render}.
68
+ #
69
+ # @return [String]
70
+ def render_string
71
+ ERB.new(template).result(binding)
72
+ end
73
+
74
+ # Returns the full path to the template, taking into accoun the gem directory
75
+ # and adding the `.erb` extension to the end.
76
+ #
77
+ # @return [String]
78
+ def full_template_path
79
+ File.join(PROJECT_ROOT, 'templates', "#{template}.erb")
80
+ end
81
+ end
82
+ end
83
+ end