vagrantup 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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