vagrant 0.2.0 → 0.3.0

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.
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