vagrantup 0.3.4 → 0.4.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +2 -2
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/config/default.rb +13 -3
  7. data/lib/vagrant.rb +10 -13
  8. data/lib/vagrant/actions/base.rb +14 -2
  9. data/lib/vagrant/actions/box/download.rb +2 -7
  10. data/lib/vagrant/actions/box/verify.rb +1 -1
  11. data/lib/vagrant/actions/runner.rb +0 -1
  12. data/lib/vagrant/actions/vm/boot.rb +2 -6
  13. data/lib/vagrant/actions/vm/customize.rb +7 -5
  14. data/lib/vagrant/actions/vm/destroy.rb +4 -3
  15. data/lib/vagrant/actions/vm/down.rb +6 -3
  16. data/lib/vagrant/actions/vm/export.rb +2 -4
  17. data/lib/vagrant/actions/vm/forward_ports.rb +77 -16
  18. data/lib/vagrant/actions/vm/halt.rb +10 -2
  19. data/lib/vagrant/actions/vm/import.rb +2 -4
  20. data/lib/vagrant/actions/vm/move_hard_drive.rb +2 -2
  21. data/lib/vagrant/actions/vm/network.rb +120 -0
  22. data/lib/vagrant/actions/vm/package.rb +11 -7
  23. data/lib/vagrant/actions/vm/provision.rb +3 -3
  24. data/lib/vagrant/actions/vm/reload.rb +2 -9
  25. data/lib/vagrant/actions/vm/shared_folders.rb +19 -39
  26. data/lib/vagrant/actions/vm/start.rb +10 -2
  27. data/lib/vagrant/actions/vm/up.rb +5 -6
  28. data/lib/vagrant/active_list.rb +23 -13
  29. data/lib/vagrant/box.rb +2 -2
  30. data/lib/vagrant/busy.rb +3 -3
  31. data/lib/vagrant/command.rb +2 -2
  32. data/lib/vagrant/commands/base.rb +40 -20
  33. data/lib/vagrant/commands/destroy.rb +17 -3
  34. data/lib/vagrant/commands/halt.rb +23 -3
  35. data/lib/vagrant/commands/package.rb +54 -14
  36. data/lib/vagrant/commands/provision.rb +31 -0
  37. data/lib/vagrant/commands/reload.rb +16 -3
  38. data/lib/vagrant/commands/resume.rb +16 -3
  39. data/lib/vagrant/commands/ssh.rb +25 -3
  40. data/lib/vagrant/commands/ssh_config.rb +20 -5
  41. data/lib/vagrant/commands/status.rb +107 -40
  42. data/lib/vagrant/commands/suspend.rb +16 -3
  43. data/lib/vagrant/commands/up.rb +26 -7
  44. data/lib/vagrant/config.rb +82 -12
  45. data/lib/vagrant/downloaders/base.rb +8 -1
  46. data/lib/vagrant/downloaders/http.rb +31 -19
  47. data/lib/vagrant/environment.rb +146 -49
  48. data/lib/vagrant/provisioners/base.rb +19 -5
  49. data/lib/vagrant/provisioners/chef.rb +12 -4
  50. data/lib/vagrant/provisioners/chef_server.rb +13 -6
  51. data/lib/vagrant/provisioners/chef_solo.rb +7 -3
  52. data/lib/vagrant/resource_logger.rb +126 -0
  53. data/lib/vagrant/ssh.rb +109 -8
  54. data/lib/vagrant/systems/base.rb +70 -0
  55. data/lib/vagrant/systems/linux.rb +137 -0
  56. data/lib/vagrant/util.rb +1 -45
  57. data/lib/vagrant/util/error_helper.rb +13 -0
  58. data/lib/vagrant/util/glob_loader.rb +22 -0
  59. data/lib/vagrant/util/output_helper.rb +9 -0
  60. data/lib/vagrant/util/plain_logger.rb +12 -0
  61. data/lib/vagrant/util/platform.rb +7 -2
  62. data/lib/vagrant/util/template_renderer.rb +2 -2
  63. data/lib/vagrant/util/translator.rb +35 -0
  64. data/lib/vagrant/vm.rb +91 -10
  65. data/templates/crontab_entry.erb +1 -0
  66. data/templates/network_entry.erb +8 -0
  67. data/templates/ssh_config.erb +1 -0
  68. data/templates/{errors.yml → strings.yml} +111 -3
  69. data/templates/sync.erb +14 -0
  70. data/test/test_helper.rb +46 -3
  71. data/test/vagrant/actions/box/download_test.rb +0 -17
  72. data/test/vagrant/actions/vm/boot_test.rb +3 -10
  73. data/test/vagrant/actions/vm/customize_test.rb +6 -0
  74. data/test/vagrant/actions/vm/destroy_test.rb +6 -5
  75. data/test/vagrant/actions/vm/down_test.rb +5 -11
  76. data/test/vagrant/actions/vm/export_test.rb +1 -0
  77. data/test/vagrant/actions/vm/forward_ports_test.rb +92 -15
  78. data/test/vagrant/actions/vm/halt_test.rb +36 -4
  79. data/test/vagrant/actions/vm/import_test.rb +2 -0
  80. data/test/vagrant/actions/vm/network_test.rb +237 -0
  81. data/test/vagrant/actions/vm/package_test.rb +35 -5
  82. data/test/vagrant/actions/vm/provision_test.rb +3 -3
  83. data/test/vagrant/actions/vm/reload_test.rb +1 -1
  84. data/test/vagrant/actions/vm/shared_folders_test.rb +41 -74
  85. data/test/vagrant/actions/vm/start_test.rb +41 -3
  86. data/test/vagrant/actions/vm/up_test.rb +10 -21
  87. data/test/vagrant/active_list_test.rb +28 -43
  88. data/test/vagrant/commands/base_test.rb +25 -4
  89. data/test/vagrant/commands/destroy_test.rb +24 -12
  90. data/test/vagrant/commands/halt_test.rb +33 -11
  91. data/test/vagrant/commands/package_test.rb +77 -57
  92. data/test/vagrant/commands/provision_test.rb +50 -0
  93. data/test/vagrant/commands/reload_test.rb +27 -11
  94. data/test/vagrant/commands/resume_test.rb +25 -14
  95. data/test/vagrant/commands/ssh_config_test.rb +40 -17
  96. data/test/vagrant/commands/ssh_test.rb +52 -13
  97. data/test/vagrant/commands/status_test.rb +21 -1
  98. data/test/vagrant/commands/suspend_test.rb +25 -14
  99. data/test/vagrant/commands/up_test.rb +25 -19
  100. data/test/vagrant/config_test.rb +74 -18
  101. data/test/vagrant/downloaders/base_test.rb +2 -1
  102. data/test/vagrant/downloaders/http_test.rb +18 -8
  103. data/test/vagrant/environment_test.rb +245 -77
  104. data/test/vagrant/provisioners/base_test.rb +4 -4
  105. data/test/vagrant/provisioners/chef_server_test.rb +18 -7
  106. data/test/vagrant/provisioners/chef_solo_test.rb +17 -7
  107. data/test/vagrant/provisioners/chef_test.rb +22 -9
  108. data/test/vagrant/resource_logger_test.rb +144 -0
  109. data/test/vagrant/ssh_session_test.rb +46 -0
  110. data/test/vagrant/ssh_test.rb +42 -2
  111. data/test/vagrant/systems/linux_test.rb +174 -0
  112. data/test/vagrant/util/error_helper_test.rb +5 -0
  113. data/test/vagrant/util/output_helper_test.rb +5 -0
  114. data/test/vagrant/util/plain_logger_test.rb +17 -0
  115. data/test/vagrant/util/platform_test.rb +18 -0
  116. data/test/vagrant/util/{errors_test.rb → translator_test.rb} +25 -21
  117. data/test/vagrant/util_test.rb +12 -49
  118. data/test/vagrant/vm_test.rb +133 -11
  119. data/vagrant.gemspec +39 -15
  120. metadata +38 -14
  121. data/lib/vagrant/commands/down.rb +0 -16
  122. data/lib/vagrant/util/errors.rb +0 -36
  123. data/lib/vagrant/util/progress_meter.rb +0 -33
  124. data/test/vagrant/commands/down_test.rb +0 -17
  125. data/test/vagrant/util/progress_meter_test.rb +0 -33
@@ -16,6 +16,7 @@ module Vagrant
16
16
  end
17
17
 
18
18
  def provision!
19
+ verify_binary("chef-client")
19
20
  chown_provisioning_folder
20
21
  create_client_key_folder
21
22
  upload_validation_key
@@ -28,14 +29,14 @@ module Vagrant
28
29
  logger.info "Creating folder to hold client key..."
29
30
  path = Pathname.new(env.config.chef.client_key_path)
30
31
 
31
- env.ssh.execute do |ssh|
32
+ vm.ssh.execute do |ssh|
32
33
  ssh.exec!("sudo mkdir -p #{path.dirname}")
33
34
  end
34
35
  end
35
36
 
36
37
  def upload_validation_key
37
38
  logger.info "Uploading chef client validation key..."
38
- env.ssh.upload!(validation_key_path, guest_validation_key_path)
39
+ vm.ssh.upload!(validation_key_path, guest_validation_key_path)
39
40
  end
40
41
 
41
42
  def setup_server_config
@@ -49,12 +50,18 @@ module Vagrant
49
50
  end
50
51
 
51
52
  def run_chef_client
53
+ command = "cd #{env.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json"
54
+
52
55
  logger.info "Running chef-client..."
53
- env.ssh.execute do |ssh|
54
- ssh.exec!("cd #{env.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json") do |channel, data, stream|
56
+ vm.ssh.execute do |ssh|
57
+ ssh.exec!(command) do |channel, type, data|
55
58
  # TODO: Very verbose. It would be easier to save the data and only show it during
56
59
  # an error, or when verbosity level is set high
57
- logger.info("#{stream}: #{data}")
60
+ if type == :exit_status
61
+ ssh.check_exit_status(data, command)
62
+ else
63
+ logger.info("#{data}: #{type}")
64
+ end
58
65
  end
59
66
  end
60
67
  end
@@ -64,7 +71,7 @@ module Vagrant
64
71
  end
65
72
 
66
73
  def guest_validation_key_path
67
- File.join(@env.config.chef.provisioning_path, "validation.pem")
74
+ File.join(env.config.chef.provisioning_path, "validation.pem")
68
75
  end
69
76
  end
70
77
  end
@@ -8,6 +8,7 @@ module Vagrant
8
8
  end
9
9
 
10
10
  def provision!
11
+ verify_binary("chef-solo")
11
12
  chown_provisioning_folder
12
13
  setup_json
13
14
  setup_solo_config
@@ -35,12 +36,15 @@ module Vagrant
35
36
  end
36
37
 
37
38
  def run_chef_solo
39
+ command = "cd #{env.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json"
40
+
38
41
  logger.info "Running chef-solo..."
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|
42
+ vm.ssh.execute do |ssh|
43
+ ssh.exec!(command) do |channel, type, data|
41
44
  # TODO: Very verbose. It would be easier to save the data and only show it during
42
45
  # an error, or when verbosity level is set high
43
- logger.info("#{stream}: #{data}")
46
+ ssh.check_exit_status(data, command) if type == :exit_status
47
+ logger.info("#{data}: #{type}") if type != :exit_status
44
48
  end
45
49
  end
46
50
  end
@@ -0,0 +1,126 @@
1
+ require 'thread'
2
+
3
+ module Vagrant
4
+ # Represents a logger for a specific resource within Vagrant. Each
5
+ # logger should be initialized and set to represent a single
6
+ # resource. Each logged message will then appear in the following
7
+ # format:
8
+ #
9
+ # [resource] message
10
+ #
11
+ # This class is thread safe. The backing class which actually does
12
+ # all the logging IO is protected.
13
+ #
14
+ # This class also handles progress meters of multiple resources and
15
+ # handles all the proper interleaving and console updating to
16
+ # display the progress meters in a way which doesn't conflict with
17
+ # other incoming log messages.
18
+ class ResourceLogger
19
+ @@singleton_logger = nil
20
+ @@progress_reporters = nil
21
+ @@writer_lock = Mutex.new
22
+
23
+ # The resource which this logger represents.
24
+ attr_reader :resource
25
+
26
+ # The environment that this logger is part of
27
+ attr_reader :env
28
+
29
+ # The backing logger which actually handles the IO. This logger
30
+ # should be a subclass of the standard library Logger, in general.
31
+ # IMPORTANT: This logger must be thread-safe.
32
+ attr_reader :logger
33
+
34
+ class << self
35
+ # Returns a singleton logger. If one has not yet be
36
+ # instantiated, then the given environment will be used to
37
+ # create a new logger.
38
+ def singleton_logger(env=nil)
39
+ if env && env.config && env.config.loaded?
40
+ @@singleton_logger ||= Util::PlainLogger.new(env.config.vagrant.log_output)
41
+ else
42
+ Util::PlainLogger.new(nil)
43
+ end
44
+ end
45
+
46
+ # Resets the singleton logger (only used for testing).
47
+ def reset_singleton_logger!
48
+ @@singleton_logger = nil
49
+ end
50
+
51
+ # Returns the progress parts array which contains the various
52
+ # progress reporters.
53
+ def progress_reporters
54
+ @@progress_reporters ||= {}
55
+ end
56
+ end
57
+
58
+ def initialize(resource, env)
59
+ @resource = resource
60
+ @env = env
61
+ @logger = self.class.singleton_logger(env)
62
+ end
63
+
64
+ # TODO: The other logging methods.
65
+
66
+ def info(message)
67
+ @@writer_lock.synchronize do
68
+ # We clear the line in case progress reports have been going
69
+ # out.
70
+ print(cl_reset)
71
+ logger.info("[#{resource}] #{message}")
72
+ end
73
+
74
+ # Once again flush the progress reporters since we probably
75
+ # cleared any existing ones.
76
+ flush_progress
77
+ end
78
+
79
+ # Sets a progress report for the resource that this logger
80
+ # represents. This progress report is interleaved within the output.
81
+ def report_progress(progress, total, show_parts=true)
82
+ # Simply add the progress reporter to the list of progress
83
+ # reporters
84
+ self.class.progress_reporters[resource] = {
85
+ :progress => progress,
86
+ :total => total,
87
+ :show_parts => show_parts
88
+ }
89
+
90
+ # And force an update to occur
91
+ flush_progress
92
+ end
93
+
94
+ # Clears the progress report for this resource
95
+ def clear_progress
96
+ self.class.progress_reporters.delete(resource)
97
+ end
98
+
99
+ def flush_progress
100
+ # Don't do anything if there are no progress reporters
101
+ return if self.class.progress_reporters.length <= 0
102
+
103
+ @@writer_lock.synchronize do
104
+ reports = []
105
+
106
+ # First generate all the report percentages and output
107
+ self.class.progress_reporters.each do |name, data|
108
+ percent = (data[:progress].to_f / data[:total].to_f) * 100
109
+ line = "#{name}: #{percent.to_i}%"
110
+ line << " (#{data[:progress]} / #{data[:total]})" if data[:show_parts]
111
+ reports << line
112
+ end
113
+
114
+ # Output it to stdout
115
+ print "#{cl_reset}[progress] #{reports.join(" ")}"
116
+ $stdout.flush
117
+ end
118
+ end
119
+
120
+ def cl_reset
121
+ reset = "\r"
122
+ reset += "\e[0K" unless Mario::Platform.windows?
123
+ reset
124
+ end
125
+ end
126
+ end
data/lib/vagrant/ssh.rb CHANGED
@@ -48,8 +48,11 @@ module Vagrant
48
48
  Net::SSH.start(env.config.ssh.host,
49
49
  env.config[:ssh][:username],
50
50
  opts.merge( :port => port,
51
- :keys => [env.config.ssh.private_key_path])) do |ssh|
52
- yield ssh
51
+ :keys => [env.config.ssh.private_key_path],
52
+ :user_known_hosts_file => [],
53
+ :paranoid => false,
54
+ :config => false)) do |ssh|
55
+ yield SSH::Session.new(ssh)
53
56
  end
54
57
  end
55
58
 
@@ -57,9 +60,16 @@ module Vagrant
57
60
  # or StringIO, and `to` is expected to be a path. This method simply forwards
58
61
  # the arguments to `Net::SCP#upload!` so view that for more information.
59
62
  def upload!(from, to)
60
- execute do |ssh|
61
- scp = Net::SCP.new(ssh)
62
- scp.upload!(from, to)
63
+ tries = 5
64
+
65
+ begin
66
+ execute do |ssh|
67
+ scp = Net::SCP.new(ssh.session)
68
+ scp.upload!(from, to)
69
+ end
70
+ rescue IOError
71
+ retry if (tries -= 1) > 0
72
+ raise
63
73
  end
64
74
  end
65
75
 
@@ -87,12 +97,14 @@ module Vagrant
87
97
  # Checks the file permissions for the private key, resetting them
88
98
  # if needed, or on failure erroring.
89
99
  def check_key_permissions(key_path)
100
+ return if Mario::Platform.windows?
101
+
90
102
  # TODO: This only works on unix based systems for now. Windows
91
103
  # systems will need to be investigated further.
92
104
  stat = File.stat(key_path)
93
105
 
94
106
  if stat.owned? && file_perms(key_path) != "600"
95
- logger.info "Permissions on private key incorrect, fixing..."
107
+ env.logger.info "Permissions on private key incorrect, fixing..."
96
108
  File.chmod(0600, key_path)
97
109
 
98
110
  error_and_exit(:ssh_bad_permissions, :key_path => key_path) if file_perms(key_path) != "600"
@@ -112,9 +124,98 @@ module Vagrant
112
124
 
113
125
  # Returns the port which is either given in the options hash or taken from
114
126
  # the config by finding it in the forwarded ports hash based on the
115
- # `config.ssh.forwarded_port_key`
127
+ # `config.ssh.forwarded_port_key` or use the default port given by `config.ssh.port`
128
+ # when port forwarding isn't used.
116
129
  def port(opts={})
117
- opts[:port] || env.config.vm.forwarded_ports[env.config.ssh.forwarded_port_key][:hostport]
130
+ # Check if port was specified in options hash
131
+ pnum = opts[:port]
132
+ return pnum if pnum
133
+
134
+ # Check if we have an SSH forwarded port
135
+ pnum = env.vm.vm.forwarded_ports.detect do |fp|
136
+ fp.name == env.config.ssh.forwarded_port_key
137
+ end
138
+
139
+ return pnum.hostport if pnum
140
+
141
+ # Fall back to the default
142
+ return env.config.ssh.port
143
+ end
144
+ end
145
+
146
+ class SSH
147
+ # A helper class which wraps around `Net::SSH::Connection::Session`
148
+ # in order to provide basic command error checking while still
149
+ # providing access to the actual session object.
150
+ class Session
151
+ attr_reader :session
152
+
153
+ def initialize(session)
154
+ @session = session
155
+ end
156
+
157
+ # Executes a given command on the SSH session and blocks until
158
+ # the command completes. This is an almost line for line copy of
159
+ # the actual `exec!` implementation, except that this
160
+ # implementation also reports `:exit_status` to the block if given.
161
+ def exec!(command, options=nil, &block)
162
+ options = {
163
+ :error_check => true
164
+ }.merge(options || {})
165
+
166
+ block ||= Proc.new do |ch, type, data|
167
+ check_exit_status(data, command, options) if type == :exit_status && options[:error_check]
168
+
169
+ ch[:result] ||= ""
170
+ ch[:result] << data if [:stdout, :stderr].include?(type)
171
+ end
172
+
173
+ tries = 5
174
+
175
+ begin
176
+ metach = session.open_channel do |channel|
177
+ channel.exec(command) do |ch, success|
178
+ raise "could not execute command: #{command.inspect}" unless success
179
+
180
+ # Output stdout data to the block
181
+ channel.on_data do |ch2, data|
182
+ block.call(ch2, :stdout, data)
183
+ end
184
+
185
+ # Output stderr data to the block
186
+ channel.on_extended_data do |ch2, type, data|
187
+ block.call(ch2, :stderr, data)
188
+ end
189
+
190
+ # Output exit status information to the block
191
+ channel.on_request("exit-status") do |ch2, data|
192
+ block.call(ch2, :exit_status, data.read_long)
193
+ end
194
+ end
195
+ end
196
+ rescue IOError
197
+ retry if (tries -= 1) > 0
198
+ raise
199
+ end
200
+
201
+ metach.wait
202
+ metach[:result]
203
+ end
204
+
205
+ # Checks for an erroroneous exit status and raises an exception
206
+ # if so.
207
+ def check_exit_status(exit_status, command, options=nil)
208
+ if exit_status != 0
209
+ options = {
210
+ :error_key => :ssh_bad_exit_status,
211
+ :error_data => {
212
+ :command => command
213
+ }
214
+ }.merge(options || {})
215
+
216
+ raise Actions::ActionException.new(options[:error_key], options[:error_data])
217
+ end
218
+ end
118
219
  end
119
220
  end
120
221
  end
@@ -0,0 +1,70 @@
1
+ module Vagrant
2
+ module Systems
3
+ # The base class for a "system." A system represents an installed
4
+ # operating system on a given box. There are some portions of
5
+ # Vagrant which are fairly OS-specific (such as mounting shared
6
+ # folders) and while the number is few, this abstraction allows
7
+ # more obscure operating systems to be installed without having
8
+ # to directly modify Vagrant internals.
9
+ #
10
+ # Subclasses of the system base class are expected to implement
11
+ # all the methods. These methods are described in the comments
12
+ # above their definition.
13
+ #
14
+ # **This is by no means a complete specification. The methods
15
+ # required by systems can and will change at any time. Any
16
+ # changes will be noted on release notes.**
17
+ class Base
18
+ include Vagrant::Util
19
+
20
+ # The VM which this system is tied to.
21
+ attr_reader :vm
22
+
23
+ # Initializes the system. Any subclasses MUST make sure this
24
+ # method is called on the parent. Therefore, if a subclass overrides
25
+ # `initialize`, then you must call `super`.
26
+ def initialize(vm)
27
+ @vm = vm
28
+ end
29
+
30
+ # A convenience method to access the logger on the VM
31
+ # environment.
32
+ def logger
33
+ vm.env.logger
34
+ end
35
+
36
+ # Halt the machine. This method should gracefully shut down the
37
+ # operating system. This method will cause `vagrant halt` and associated
38
+ # commands to _block_, meaning that if the machine doesn't halt
39
+ # in a reasonable amount of time, this method should just return.
40
+ #
41
+ # If when this method returns, the machine's state isn't "powered_off,"
42
+ # Vagrant will proceed to forcefully shut the machine down.
43
+ def halt; end
44
+
45
+ # Mounts a shared folder. This method is called by the shared
46
+ # folder action with an open SSH session (passed in as `ssh`).
47
+ # This method should create, mount, and properly set permissions
48
+ # on the shared folder. This method should also properly
49
+ # adhere to any configuration values such as `shared_folder_uid`
50
+ # on `config.vm`.
51
+ #
52
+ # @param [Object] ssh The Net::SSH session.
53
+ # @param [String] name The name of the shared folder.
54
+ # @param [String] guestpath The path on the machine which the user
55
+ # wants the folder mounted.
56
+ def mount_shared_folder(ssh, name, guestpath); end
57
+
58
+ # Prepares the system for host only networks. This is called
59
+ # once prior to any `enable_host_only_network` calls.
60
+ def prepare_host_only_network; end
61
+
62
+ # Setup the system by adding a new host only network. This
63
+ # method should configure and bring up the interface for the
64
+ # given options.
65
+ #
66
+ # @param [Hash] net_options The options for the network.
67
+ def enable_host_only_network(net_options); end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,137 @@
1
+ module Vagrant
2
+ module Systems
3
+ # A general Vagrant system implementation for "linux." In general,
4
+ # any linux-based OS will work fine with this system, although its
5
+ # not tested exhaustively. BSD or other based systems may work as
6
+ # well, but that hasn't been tested at all.
7
+ #
8
+ # At any rate, this system implementation should server as an
9
+ # example of how to implement any custom systems necessary.
10
+ class Linux < Base
11
+ # A custom config class which will be made accessible via `config.linux`
12
+ # This is not necessary for all system implementers, of course. However,
13
+ # generally, Vagrant tries to make almost every aspect of its execution
14
+ # configurable, and this assists that goal.
15
+ class LinuxConfig < Vagrant::Config::Base
16
+ attr_accessor :halt_timeout
17
+ attr_accessor :halt_check_interval
18
+
19
+ def initialize
20
+ @halt_timeout = 15
21
+ @halt_check_interval = 1
22
+ end
23
+ end
24
+
25
+ # Register config class
26
+ Config.configures :linux, LinuxConfig
27
+
28
+ #-------------------------------------------------------------------
29
+ # Overridden methods
30
+ #-------------------------------------------------------------------
31
+ def halt
32
+ logger.info "Attempting graceful shutdown of linux..."
33
+ vm.ssh.execute do |ssh|
34
+ ssh.exec!("sudo halt")
35
+ end
36
+
37
+ # Wait until the VM's state is actually powered off. If this doesn't
38
+ # occur within a reasonable amount of time (15 seconds by default),
39
+ # then simply return and allow Vagrant to kill the machine.
40
+ count = 0
41
+ while vm.vm.state != :powered_off
42
+ count += 1
43
+
44
+ return if count >= vm.env.config.linux.halt_timeout
45
+ sleep vm.env.config.linux.halt_check_interval
46
+ end
47
+ end
48
+
49
+ def mount_shared_folder(ssh, name, guestpath)
50
+ ssh.exec!("sudo mkdir -p #{guestpath}")
51
+ mount_folder(ssh, name, guestpath)
52
+ chown(ssh, guestpath)
53
+ end
54
+
55
+ def create_sync(ssh, opts)
56
+ crontab_entry = render_crontab_entry(opts.merge(:syncopts => config.vm.sync_opts,
57
+ :scriptname => config.vm.sync_script))
58
+
59
+ ssh.exec!("sudo mkdir -p #{opts[:syncpath]}")
60
+ chown(ssh, opts[:syncpath])
61
+ ssh.exec!("sudo echo \"#{crontab_entry}\" >> #{config.vm.sync_crontab_entry_file}")
62
+ ssh.exec!("crontab #{config.vm.sync_crontab_entry_file}")
63
+ end
64
+
65
+ def prepare_sync(ssh)
66
+ logger.info "Preparing system for sync..."
67
+ vm.ssh.upload!(StringIO.new(render_sync), config.vm.sync_script)
68
+ ssh.exec!("sudo chmod +x #{config.vm.sync_script}")
69
+ ssh.exec!("sudo rm #{config.vm.sync_crontab_entry_file}", :error_check => false)
70
+ end
71
+
72
+ def prepare_host_only_network
73
+ # Remove any previous host only network additions to the
74
+ # interface file.
75
+ vm.ssh.execute do |ssh|
76
+ # Verify debian/ubuntu
77
+ ssh.exec!("cat /etc/debian_version", :error_key => :network_not_debian)
78
+
79
+ # Clear out any previous entries
80
+ ssh.exec!("sudo sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces")
81
+ ssh.exec!("sudo su -c 'cat /tmp/vagrant-network-interfaces > /etc/network/interfaces'")
82
+ end
83
+ end
84
+
85
+ def enable_host_only_network(net_options)
86
+ entry = TemplateRenderer.render('network_entry', :net_options => net_options)
87
+ vm.ssh.upload!(StringIO.new(entry), "/tmp/vagrant-network-entry")
88
+
89
+ vm.ssh.execute do |ssh|
90
+ ssh.exec!("sudo su -c 'cat /tmp/vagrant-network-entry >> /etc/network/interfaces'")
91
+ ssh.exec!("sudo /etc/init.d/networking restart")
92
+ end
93
+ end
94
+
95
+ #-------------------------------------------------------------------
96
+ # "Private" methods which assist above methods
97
+ #-------------------------------------------------------------------
98
+ def mount_folder(ssh, name, guestpath, sleeptime=5)
99
+ # Determine the permission string to attach to the mount command
100
+ perms = []
101
+ perms << "uid=#{vm.env.config.vm.shared_folder_uid}"
102
+ perms << "gid=#{vm.env.config.vm.shared_folder_gid}"
103
+ perms = " -o #{perms.join(",")}" if !perms.empty?
104
+
105
+ attempts = 0
106
+ while true
107
+ result = ssh.exec!("sudo mount -t vboxsf#{perms} #{name} #{guestpath}") do |ch, type, data|
108
+ # net/ssh returns the value in ch[:result] (based on looking at source)
109
+ ch[:result] = !!(type == :stderr && data =~ /No such device/i)
110
+ end
111
+
112
+ break unless result
113
+
114
+ attempts += 1
115
+ raise Actions::ActionException.new(:vm_mount_fail) if attempts >= 10
116
+ sleep sleeptime
117
+ end
118
+ end
119
+
120
+ def chown(ssh, dir)
121
+ ssh.exec!("sudo chown #{config.ssh.username} #{dir}")
122
+ end
123
+
124
+ def config
125
+ vm.env.config
126
+ end
127
+
128
+ def render_sync
129
+ TemplateRenderer.render('sync')
130
+ end
131
+
132
+ def render_crontab_entry(opts)
133
+ TemplateRenderer.render('crontab_entry', opts)
134
+ end
135
+ end
136
+ end
137
+ end