rvagrant 0.8.7.dev
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.
- data/.gitignore +25 -0
- data/.yardopts +1 -0
- data/CHANGELOG.md +386 -0
- data/Gemfile +21 -0
- data/LICENSE +21 -0
- data/README.md +61 -0
- data/Rakefile +11 -0
- data/bin/vagrant +28 -0
- data/config/default.rb +38 -0
- data/contrib/README.md +12 -0
- data/contrib/emacs/vagrant.el +8 -0
- data/contrib/vim/vagrantfile.vim +9 -0
- data/keys/README.md +17 -0
- data/keys/vagrant +27 -0
- data/keys/vagrant.ppk +26 -0
- data/keys/vagrant.pub +1 -0
- data/lib/vagrant.rb +44 -0
- data/lib/vagrant/action.rb +138 -0
- data/lib/vagrant/action/box.rb +11 -0
- data/lib/vagrant/action/box/destroy.rb +21 -0
- data/lib/vagrant/action/box/download.rb +72 -0
- data/lib/vagrant/action/box/package.rb +19 -0
- data/lib/vagrant/action/box/unpackage.rb +55 -0
- data/lib/vagrant/action/box/verify.rb +23 -0
- data/lib/vagrant/action/builder.rb +124 -0
- data/lib/vagrant/action/builtin.rb +108 -0
- data/lib/vagrant/action/env.rb +7 -0
- data/lib/vagrant/action/env/set.rb +18 -0
- data/lib/vagrant/action/environment.rb +50 -0
- data/lib/vagrant/action/general.rb +8 -0
- data/lib/vagrant/action/general/package.rb +113 -0
- data/lib/vagrant/action/general/validate.rb +19 -0
- data/lib/vagrant/action/vm.rb +34 -0
- data/lib/vagrant/action/vm/boot.rb +48 -0
- data/lib/vagrant/action/vm/check_accessible.rb +23 -0
- data/lib/vagrant/action/vm/check_box.rb +28 -0
- data/lib/vagrant/action/vm/check_guest_additions.rb +35 -0
- data/lib/vagrant/action/vm/clean_machine_folder.rb +43 -0
- data/lib/vagrant/action/vm/clear_forwarded_ports.rb +26 -0
- data/lib/vagrant/action/vm/clear_nfs_exports.rb +20 -0
- data/lib/vagrant/action/vm/clear_shared_folders.rb +27 -0
- data/lib/vagrant/action/vm/customize.rb +26 -0
- data/lib/vagrant/action/vm/destroy.rb +19 -0
- data/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb +30 -0
- data/lib/vagrant/action/vm/discard_state.rb +22 -0
- data/lib/vagrant/action/vm/export.rb +52 -0
- data/lib/vagrant/action/vm/forward_ports.rb +133 -0
- data/lib/vagrant/action/vm/forward_ports_helpers.rb +28 -0
- data/lib/vagrant/action/vm/halt.rb +29 -0
- data/lib/vagrant/action/vm/host_name.rb +21 -0
- data/lib/vagrant/action/vm/import.rb +40 -0
- data/lib/vagrant/action/vm/match_mac_address.rb +26 -0
- data/lib/vagrant/action/vm/modify.rb +37 -0
- data/lib/vagrant/action/vm/network.rb +146 -0
- data/lib/vagrant/action/vm/nfs.rb +160 -0
- data/lib/vagrant/action/vm/nfs_helpers.rb +11 -0
- data/lib/vagrant/action/vm/package.rb +23 -0
- data/lib/vagrant/action/vm/package_vagrantfile.rb +33 -0
- data/lib/vagrant/action/vm/provision.rb +34 -0
- data/lib/vagrant/action/vm/provisioner_cleanup.rb +26 -0
- data/lib/vagrant/action/vm/resume.rb +20 -0
- data/lib/vagrant/action/vm/share_folders.rb +78 -0
- data/lib/vagrant/action/vm/suspend.rb +20 -0
- data/lib/vagrant/action/warden.rb +85 -0
- data/lib/vagrant/box.rb +90 -0
- data/lib/vagrant/box_collection.rb +53 -0
- data/lib/vagrant/cli.rb +55 -0
- data/lib/vagrant/command.rb +25 -0
- data/lib/vagrant/command/base.rb +106 -0
- data/lib/vagrant/command/box.rb +33 -0
- data/lib/vagrant/command/destroy.rb +17 -0
- data/lib/vagrant/command/group_base.rb +107 -0
- data/lib/vagrant/command/halt.rb +18 -0
- data/lib/vagrant/command/helpers.rb +33 -0
- data/lib/vagrant/command/init.rb +14 -0
- data/lib/vagrant/command/named_base.rb +14 -0
- data/lib/vagrant/command/package.rb +41 -0
- data/lib/vagrant/command/provision.rb +23 -0
- data/lib/vagrant/command/reload.rb +17 -0
- data/lib/vagrant/command/resume.rb +17 -0
- data/lib/vagrant/command/ssh.rb +49 -0
- data/lib/vagrant/command/ssh_config.rb +29 -0
- data/lib/vagrant/command/status.rb +31 -0
- data/lib/vagrant/command/suspend.rb +17 -0
- data/lib/vagrant/command/up.rb +19 -0
- data/lib/vagrant/command/upgrade_to_060.rb +45 -0
- data/lib/vagrant/command/version.rb +13 -0
- data/lib/vagrant/config.rb +123 -0
- data/lib/vagrant/config/base.rb +85 -0
- data/lib/vagrant/config/error_recorder.rb +19 -0
- data/lib/vagrant/config/nfs.rb +10 -0
- data/lib/vagrant/config/package.rb +9 -0
- data/lib/vagrant/config/ssh.rb +38 -0
- data/lib/vagrant/config/top.rb +61 -0
- data/lib/vagrant/config/vagrant.rb +17 -0
- data/lib/vagrant/config/vm.rb +148 -0
- data/lib/vagrant/config/vm/provisioner.rb +56 -0
- data/lib/vagrant/config/vm/sub_vm.rb +17 -0
- data/lib/vagrant/data_store.rb +70 -0
- data/lib/vagrant/downloaders.rb +7 -0
- data/lib/vagrant/downloaders/base.rb +23 -0
- data/lib/vagrant/downloaders/file.rb +22 -0
- data/lib/vagrant/downloaders/http.rb +70 -0
- data/lib/vagrant/environment.rb +516 -0
- data/lib/vagrant/errors.rb +352 -0
- data/lib/vagrant/hosts.rb +9 -0
- data/lib/vagrant/hosts/arch.rb +27 -0
- data/lib/vagrant/hosts/base.rb +76 -0
- data/lib/vagrant/hosts/bsd.rb +58 -0
- data/lib/vagrant/hosts/fedora.rb +11 -0
- data/lib/vagrant/hosts/linux.rb +69 -0
- data/lib/vagrant/plugin.rb +79 -0
- data/lib/vagrant/provisioners.rb +9 -0
- data/lib/vagrant/provisioners/base.rb +67 -0
- data/lib/vagrant/provisioners/chef.rb +155 -0
- data/lib/vagrant/provisioners/chef_client.rb +120 -0
- data/lib/vagrant/provisioners/chef_solo.rb +135 -0
- data/lib/vagrant/provisioners/puppet.rb +137 -0
- data/lib/vagrant/provisioners/puppet_server.rb +55 -0
- data/lib/vagrant/provisioners/shell.rb +96 -0
- data/lib/vagrant/ssh.rb +224 -0
- data/lib/vagrant/ssh/session.rb +136 -0
- data/lib/vagrant/systems.rb +13 -0
- data/lib/vagrant/systems/arch.rb +34 -0
- data/lib/vagrant/systems/base.rb +87 -0
- data/lib/vagrant/systems/debian.rb +36 -0
- data/lib/vagrant/systems/freebsd.rb +84 -0
- data/lib/vagrant/systems/gentoo.rb +27 -0
- data/lib/vagrant/systems/linux.rb +82 -0
- data/lib/vagrant/systems/linux/config.rb +21 -0
- data/lib/vagrant/systems/linux/error.rb +9 -0
- data/lib/vagrant/systems/redhat.rb +48 -0
- data/lib/vagrant/systems/solaris.rb +67 -0
- data/lib/vagrant/systems/suse.rb +9 -0
- data/lib/vagrant/systems/ubuntu.rb +17 -0
- data/lib/vagrant/test_helpers.rb +128 -0
- data/lib/vagrant/ui.rb +81 -0
- data/lib/vagrant/util.rb +13 -0
- data/lib/vagrant/util/busy.rb +59 -0
- data/lib/vagrant/util/counter.rb +24 -0
- data/lib/vagrant/util/hash_with_indifferent_access.rb +63 -0
- data/lib/vagrant/util/platform.rb +57 -0
- data/lib/vagrant/util/retryable.rb +25 -0
- data/lib/vagrant/util/safe_exec.rb +35 -0
- data/lib/vagrant/util/stacked_proc_runner.rb +35 -0
- data/lib/vagrant/util/template_renderer.rb +83 -0
- data/lib/vagrant/version.rb +6 -0
- data/lib/vagrant/vm.rb +181 -0
- data/templates/chef_server_client.erb +32 -0
- data/templates/chef_solo_solo.erb +23 -0
- data/templates/commands/init/Vagrantfile.erb +86 -0
- data/templates/config/validation_failed.erb +7 -0
- data/templates/locales/en.yml +556 -0
- data/templates/network_entry_arch.erb +9 -0
- data/templates/network_entry_debian.erb +8 -0
- data/templates/network_entry_gentoo.erb +5 -0
- data/templates/network_entry_redhat.erb +9 -0
- data/templates/nfs/exports.erb +5 -0
- data/templates/nfs/exports_linux.erb +5 -0
- data/templates/package_Vagrantfile.erb +11 -0
- data/templates/ssh_config.erb +15 -0
- data/test/unit/locales/en.yml +8 -0
- data/test/unit/test_helper.rb +28 -0
- data/test/unit/vagrant/action/box/destroy_test.rb +18 -0
- data/test/unit/vagrant/action/box/download_test.rb +125 -0
- data/test/unit/vagrant/action/box/package_test.rb +25 -0
- data/test/unit/vagrant/action/box/unpackage_test.rb +84 -0
- data/test/unit/vagrant/action/box/verify_test.rb +30 -0
- data/test/unit/vagrant/action/builder_test.rb +207 -0
- data/test/unit/vagrant/action/env/set_test.rb +24 -0
- data/test/unit/vagrant/action/environment_test.rb +27 -0
- data/test/unit/vagrant/action/general/package_test.rb +268 -0
- data/test/unit/vagrant/action/general/validate_test.rb +31 -0
- data/test/unit/vagrant/action/vm/boot_test.rb +66 -0
- data/test/unit/vagrant/action/vm/check_accessible_test.rb +61 -0
- data/test/unit/vagrant/action/vm/check_box_test.rb +56 -0
- data/test/unit/vagrant/action/vm/check_guest_additions_test.rb +9 -0
- data/test/unit/vagrant/action/vm/clean_machine_folder_test.rb +84 -0
- data/test/unit/vagrant/action/vm/clear_forwarded_ports_test.rb +52 -0
- data/test/unit/vagrant/action/vm/clear_nfs_exports_test.rb +22 -0
- data/test/unit/vagrant/action/vm/clear_shared_folders_test.rb +40 -0
- data/test/unit/vagrant/action/vm/customize_test.rb +37 -0
- data/test/unit/vagrant/action/vm/destroy_test.rb +25 -0
- data/test/unit/vagrant/action/vm/destroy_unused_network_interfaces_test.rb +49 -0
- data/test/unit/vagrant/action/vm/discard_state_test.rb +45 -0
- data/test/unit/vagrant/action/vm/export_test.rb +107 -0
- data/test/unit/vagrant/action/vm/forward_ports_helpers_test.rb +77 -0
- data/test/unit/vagrant/action/vm/forward_ports_test.rb +213 -0
- data/test/unit/vagrant/action/vm/halt_test.rb +79 -0
- data/test/unit/vagrant/action/vm/host_name_test.rb +36 -0
- data/test/unit/vagrant/action/vm/import_test.rb +66 -0
- data/test/unit/vagrant/action/vm/match_mac_address_test.rb +40 -0
- data/test/unit/vagrant/action/vm/modify_test.rb +38 -0
- data/test/unit/vagrant/action/vm/network_test.rb +286 -0
- data/test/unit/vagrant/action/vm/nfs_helpers_test.rb +26 -0
- data/test/unit/vagrant/action/vm/nfs_test.rb +260 -0
- data/test/unit/vagrant/action/vm/package_test.rb +25 -0
- data/test/unit/vagrant/action/vm/package_vagrantfile_test.rb +46 -0
- data/test/unit/vagrant/action/vm/provision_test.rb +65 -0
- data/test/unit/vagrant/action/vm/provisioner_cleanup_test.rb +56 -0
- data/test/unit/vagrant/action/vm/resume_test.rb +35 -0
- data/test/unit/vagrant/action/vm/share_folders_test.rb +144 -0
- data/test/unit/vagrant/action/vm/suspend_test.rb +35 -0
- data/test/unit/vagrant/action/warden_test.rb +125 -0
- data/test/unit/vagrant/action_test.rb +89 -0
- data/test/unit/vagrant/box_collection_test.rb +45 -0
- data/test/unit/vagrant/box_test.rb +74 -0
- data/test/unit/vagrant/cli_test.rb +35 -0
- data/test/unit/vagrant/command/base_test.rb +23 -0
- data/test/unit/vagrant/command/group_base_test.rb +15 -0
- data/test/unit/vagrant/command/helpers_test.rb +88 -0
- data/test/unit/vagrant/command/package_test.rb +27 -0
- data/test/unit/vagrant/config/base_test.rb +52 -0
- data/test/unit/vagrant/config/error_recorder_test.rb +18 -0
- data/test/unit/vagrant/config/ssh_test.rb +12 -0
- data/test/unit/vagrant/config/vagrant_test.rb +35 -0
- data/test/unit/vagrant/config/vm/provisioner_test.rb +92 -0
- data/test/unit/vagrant/config/vm_test.rb +56 -0
- data/test/unit/vagrant/config_test.rb +162 -0
- data/test/unit/vagrant/data_store_test.rb +77 -0
- data/test/unit/vagrant/downloaders/base_test.rb +28 -0
- data/test/unit/vagrant/downloaders/file_test.rb +48 -0
- data/test/unit/vagrant/downloaders/http_test.rb +82 -0
- data/test/unit/vagrant/environment_test.rb +598 -0
- data/test/unit/vagrant/errors_test.rb +42 -0
- data/test/unit/vagrant/hosts/base_test.rb +46 -0
- data/test/unit/vagrant/hosts/bsd_test.rb +53 -0
- data/test/unit/vagrant/hosts/linux_test.rb +54 -0
- data/test/unit/vagrant/plugin_test.rb +9 -0
- data/test/unit/vagrant/provisioners/base_test.rb +63 -0
- data/test/unit/vagrant/provisioners/chef_client_test.rb +190 -0
- data/test/unit/vagrant/provisioners/chef_solo_test.rb +114 -0
- data/test/unit/vagrant/provisioners/chef_test.rb +202 -0
- data/test/unit/vagrant/provisioners/puppet_server_test.rb +68 -0
- data/test/unit/vagrant/provisioners/puppet_test.rb +182 -0
- data/test/unit/vagrant/provisioners/shell_test.rb +79 -0
- data/test/unit/vagrant/ssh/session_test.rb +40 -0
- data/test/unit/vagrant/ssh_test.rb +307 -0
- data/test/unit/vagrant/systems/base_test.rb +18 -0
- data/test/unit/vagrant/systems/linux_test.rb +104 -0
- data/test/unit/vagrant/ui_test.rb +29 -0
- data/test/unit/vagrant/util/busy_test.rb +106 -0
- data/test/unit/vagrant/util/counter_test.rb +29 -0
- data/test/unit/vagrant/util/hash_with_indifferent_access_test.rb +39 -0
- data/test/unit/vagrant/util/platform_test.rb +18 -0
- data/test/unit/vagrant/util/retryable_test.rb +50 -0
- data/test/unit/vagrant/util/stacked_proc_runner_test.rb +43 -0
- data/test/unit/vagrant/util/template_renderer_test.rb +145 -0
- data/test/unit/vagrant/vm_test.rb +300 -0
- data/vagrant.gemspec +35 -0
- metadata +431 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module Vagrant
|
|
2
|
+
module Provisioners
|
|
3
|
+
class Shell < Base
|
|
4
|
+
register :shell
|
|
5
|
+
|
|
6
|
+
class Config < Vagrant::Config::Base
|
|
7
|
+
attr_accessor :inline
|
|
8
|
+
attr_accessor :path
|
|
9
|
+
attr_accessor :upload_path
|
|
10
|
+
attr_accessor :args
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@inline = nil
|
|
14
|
+
@path = nil
|
|
15
|
+
@upload_path = "/tmp/vagrant-shell"
|
|
16
|
+
@args = nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def expanded_path
|
|
20
|
+
Pathname.new(path).expand_path(env.root_path) if path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate(errors)
|
|
24
|
+
super
|
|
25
|
+
|
|
26
|
+
# Validate that the parameters are properly set
|
|
27
|
+
if path && inline
|
|
28
|
+
errors.add(I18n.t("vagrant.provisioners.shell.path_and_inline_set"))
|
|
29
|
+
elsif !path && !inline
|
|
30
|
+
errors.add(I18n.t("vagrant.provisioners.shell.no_path_or_inline"))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Validate the existence of a script to upload
|
|
34
|
+
if path && !expanded_path.file?
|
|
35
|
+
errors.add(I18n.t("vagrant.provisioners.shell.path_invalid", :path => expanded_path))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# There needs to be a path to upload the script to
|
|
39
|
+
if !upload_path
|
|
40
|
+
errors.add(I18n.t("vagrant.provisioners.shell.upload_path_not_set"))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# If there are args and its not a string, that is a problem
|
|
44
|
+
if args && !args.is_a?(String)
|
|
45
|
+
errors.add(I18n.t("vagrant.provisioners.shell.args_not_string"))
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# This method yields the path to a script to upload and execute
|
|
51
|
+
# on the remote server. This method will properly clean up the
|
|
52
|
+
# script file if needed.
|
|
53
|
+
def with_script_file
|
|
54
|
+
if config.path
|
|
55
|
+
# Just yield the path to that file...
|
|
56
|
+
yield config.expanded_path
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Otherwise we have an inline script, we need to Tempfile it,
|
|
61
|
+
# and handle it specially...
|
|
62
|
+
file = Tempfile.new('vagrant-shell')
|
|
63
|
+
begin
|
|
64
|
+
file.write(config.inline)
|
|
65
|
+
file.fsync
|
|
66
|
+
yield file.path
|
|
67
|
+
ensure
|
|
68
|
+
file.close
|
|
69
|
+
file.unlink
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def provision!
|
|
74
|
+
args = ""
|
|
75
|
+
args = " #{config.args}" if config.args
|
|
76
|
+
commands = ["chmod +x #{config.upload_path}", "#{config.upload_path}#{args}"]
|
|
77
|
+
|
|
78
|
+
with_script_file do |path|
|
|
79
|
+
# Upload the script to the VM
|
|
80
|
+
vm.ssh.upload!(path.to_s, config.upload_path)
|
|
81
|
+
|
|
82
|
+
# Execute it with sudo
|
|
83
|
+
vm.ssh.execute do |ssh|
|
|
84
|
+
ssh.sudo!(commands) do |ch, type, data|
|
|
85
|
+
if type == :exit_status
|
|
86
|
+
ssh.check_exit_status(data, commands)
|
|
87
|
+
else
|
|
88
|
+
env.ui.info(data)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/vagrant/ssh.rb
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
require 'net/ssh'
|
|
2
|
+
require 'net/scp'
|
|
3
|
+
|
|
4
|
+
module Vagrant
|
|
5
|
+
# Manages SSH access to a specific environment. Allows an environment to
|
|
6
|
+
# replace the process with SSH itself, run a specific set of commands,
|
|
7
|
+
# upload files, or even check if a host is up.
|
|
8
|
+
class SSH
|
|
9
|
+
# Autoload this guy because he is really only used in one location
|
|
10
|
+
# and not for every Vagrant command.
|
|
11
|
+
autoload :Session, 'vagrant/ssh/session'
|
|
12
|
+
|
|
13
|
+
include Util::Retryable
|
|
14
|
+
include Util::SafeExec
|
|
15
|
+
|
|
16
|
+
# Reference back up to the environment which this SSH object belongs
|
|
17
|
+
# to
|
|
18
|
+
attr_accessor :env
|
|
19
|
+
|
|
20
|
+
def initialize(environment)
|
|
21
|
+
@env = environment
|
|
22
|
+
@current_session = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Connects to the environment's virtual machine, replacing the ruby
|
|
26
|
+
# process with an SSH process. This method optionally takes a hash
|
|
27
|
+
# of options which override the configuration values.
|
|
28
|
+
def connect(opts={})
|
|
29
|
+
if Util::Platform.windows?
|
|
30
|
+
raise Errors::SSHUnavailableWindows, :key_path => env.config.ssh.private_key_path,
|
|
31
|
+
:ssh_port => port(opts)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
|
|
35
|
+
|
|
36
|
+
options = {}
|
|
37
|
+
options[:port] = port(opts)
|
|
38
|
+
[:host, :username, :private_key_path].each do |param|
|
|
39
|
+
options[param] = opts[param] || env.config.ssh.send(param)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
check_key_permissions(options[:private_key_path])
|
|
43
|
+
|
|
44
|
+
# Command line options
|
|
45
|
+
command_options = ["-p #{options[:port]}", "-o UserKnownHostsFile=/dev/null",
|
|
46
|
+
"-o StrictHostKeyChecking=no", "-o IdentitiesOnly=yes",
|
|
47
|
+
"-i #{options[:private_key_path]}", "-o LogLevel=ERROR"]
|
|
48
|
+
command_options << "-o ForwardAgent=yes" if env.config.ssh.forward_agent
|
|
49
|
+
|
|
50
|
+
if env.config.ssh.forward_x11
|
|
51
|
+
# Both are required so that no warnings are shown regarding X11
|
|
52
|
+
command_options << "-o ForwardX11=yes"
|
|
53
|
+
command_options << "-o ForwardX11Trusted=yes"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
|
|
57
|
+
# (GH-51). As a workaround, we fork and wait. On all other platforms,
|
|
58
|
+
# we simply exec.
|
|
59
|
+
command = "ssh #{command_options.join(" ")} #{options[:username]}@#{options[:host]}".strip
|
|
60
|
+
env.logger.info("ssh") { "Invoking SSH: #{command}" }
|
|
61
|
+
safe_exec(command)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Opens an SSH connection to this environment's virtual machine and yields
|
|
65
|
+
# a Net::SSH object which can be used to execute remote commands.
|
|
66
|
+
def execute(opts={})
|
|
67
|
+
# Check the key permissions to avoid SSH hangs
|
|
68
|
+
check_key_permissions(env.config.ssh.private_key_path)
|
|
69
|
+
|
|
70
|
+
# Merge in any additional options
|
|
71
|
+
opts = opts.dup
|
|
72
|
+
opts[:forward_agent] = true if env.config.ssh.forward_agent
|
|
73
|
+
opts[:port] ||= port
|
|
74
|
+
|
|
75
|
+
# Check if we have a currently open SSH session which has the
|
|
76
|
+
# same options, and use that if possible.
|
|
77
|
+
#
|
|
78
|
+
# NOTE: This is experimental and unstable. Therefore it is disabled
|
|
79
|
+
# by default.
|
|
80
|
+
session, options = nil
|
|
81
|
+
session, options = @current_session if env.config.vagrant.ssh_session_cache
|
|
82
|
+
|
|
83
|
+
if session && options == opts
|
|
84
|
+
# Verify that the SSH session is still valid
|
|
85
|
+
begin
|
|
86
|
+
session.exec!("echo foo")
|
|
87
|
+
rescue IOError
|
|
88
|
+
# Reset the session, we need to reconnect
|
|
89
|
+
session = nil
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if !session || options != opts
|
|
94
|
+
env.logger.info("ssh") { "Connecting to SSH: #{env.config.ssh.host} #{opts[:port]}" }
|
|
95
|
+
|
|
96
|
+
# The exceptions which are acceptable to retry on during
|
|
97
|
+
# attempts to connect to SSH
|
|
98
|
+
exceptions = [Errno::ECONNREFUSED, Net::SSH::Disconnect]
|
|
99
|
+
|
|
100
|
+
# Connect to SSH and gather the session
|
|
101
|
+
session = retryable(:tries => 5, :on => exceptions) do
|
|
102
|
+
connection = Net::SSH.start(env.config.ssh.host,
|
|
103
|
+
env.config.ssh.username,
|
|
104
|
+
opts.merge( :keys => [env.config.ssh.private_key_path],
|
|
105
|
+
:keys_only => true,
|
|
106
|
+
:user_known_hosts_file => [],
|
|
107
|
+
:paranoid => false,
|
|
108
|
+
:config => false))
|
|
109
|
+
SSH::Session.new(connection, env)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Save the new session along with the options which created it
|
|
113
|
+
@current_session = [session, opts]
|
|
114
|
+
else
|
|
115
|
+
env.logger.info("ssh") { "Using cached SSH session: #{session}" }
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Yield our session for executing
|
|
119
|
+
return yield session if block_given?
|
|
120
|
+
rescue Errno::ECONNREFUSED
|
|
121
|
+
raise Errors::SSHConnectionRefused
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Uploads a file from `from` to `to`. `from` is expected to be a filename
|
|
125
|
+
# or StringIO, and `to` is expected to be a path. This method simply forwards
|
|
126
|
+
# the arguments to `Net::SCP#upload!` so view that for more information.
|
|
127
|
+
def upload!(from, to)
|
|
128
|
+
retryable(:tries => 5, :on => IOError) do
|
|
129
|
+
execute do |ssh|
|
|
130
|
+
scp = Net::SCP.new(ssh.session)
|
|
131
|
+
scp.upload!(from, to)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Checks if this environment's machine is up (i.e. responding to SSH).
|
|
137
|
+
#
|
|
138
|
+
# @return [Boolean]
|
|
139
|
+
def up?
|
|
140
|
+
# We have to determine the port outside of the block since it uses
|
|
141
|
+
# API calls which can only be used from the main thread in JRuby on
|
|
142
|
+
# Windows
|
|
143
|
+
ssh_port = port
|
|
144
|
+
|
|
145
|
+
require 'timeout'
|
|
146
|
+
Timeout.timeout(env.config.ssh.timeout) do
|
|
147
|
+
execute(:timeout => env.config.ssh.timeout,
|
|
148
|
+
:port => ssh_port) { |ssh| }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
true
|
|
152
|
+
rescue Net::SSH::AuthenticationFailed
|
|
153
|
+
raise Errors::SSHAuthenticationFailed
|
|
154
|
+
rescue Timeout::Error, Errno::ECONNREFUSED, Net::SSH::Disconnect,
|
|
155
|
+
Errors::SSHConnectionRefused, Net::SSH::AuthenticationFailed
|
|
156
|
+
return false
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Checks the file permissions for the private key, resetting them
|
|
160
|
+
# if needed, or on failure erroring.
|
|
161
|
+
def check_key_permissions(key_path)
|
|
162
|
+
# Windows systems don't have this issue
|
|
163
|
+
return if Util::Platform.windows?
|
|
164
|
+
|
|
165
|
+
env.logger.info("ssh") { "Checking key permissions: #{key_path}" }
|
|
166
|
+
|
|
167
|
+
stat = File.stat(key_path)
|
|
168
|
+
|
|
169
|
+
if stat.owned? && file_perms(key_path) != "600"
|
|
170
|
+
env.logger.info("ssh") { "Attempting to correct key permissions to 0600" }
|
|
171
|
+
|
|
172
|
+
File.chmod(0600, key_path)
|
|
173
|
+
raise Errors::SSHKeyBadPermissions, :key_path => key_path if file_perms(key_path) != "600"
|
|
174
|
+
end
|
|
175
|
+
rescue Errno::EPERM
|
|
176
|
+
# This shouldn't happen since we verify we own the file, but just
|
|
177
|
+
# in case.
|
|
178
|
+
raise Errors::SSHKeyBadPermissions, :key_path => key_path
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Returns the file permissions of a given file. This is fairly unix specific
|
|
182
|
+
# and probably doesn't belong in this class. Will be refactored out later.
|
|
183
|
+
def file_perms(path)
|
|
184
|
+
perms = sprintf("%o", File.stat(path).mode)
|
|
185
|
+
perms.reverse[0..2].reverse
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Returns the port which is either given in the options hash or taken from
|
|
189
|
+
# the config by finding it in the forwarded ports hash based on the
|
|
190
|
+
# `config.ssh.forwarded_port_key`.
|
|
191
|
+
def port(opts={})
|
|
192
|
+
# Check if port was specified in options hash
|
|
193
|
+
return opts[:port] if opts[:port]
|
|
194
|
+
|
|
195
|
+
# Check if a port was specified in the config
|
|
196
|
+
return env.config.ssh.port if env.config.ssh.port
|
|
197
|
+
|
|
198
|
+
# Check if we have an SSH forwarded port
|
|
199
|
+
pnum_by_name = nil
|
|
200
|
+
pnum_by_destination = nil
|
|
201
|
+
env.vm.vm.network_adapters.each do |na|
|
|
202
|
+
# Look for the port number by name...
|
|
203
|
+
pnum_by_name = na.nat_driver.forwarded_ports.detect do |fp|
|
|
204
|
+
fp.name == env.config.ssh.forwarded_port_key
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Look for the port number by destination...
|
|
208
|
+
pnum_by_destination = na.nat_driver.forwarded_ports.detect do |fp|
|
|
209
|
+
fp.guestport == env.config.ssh.forwarded_port_destination
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# pnum_by_name is what we're looking for here, so break early
|
|
213
|
+
# if we have it.
|
|
214
|
+
break if pnum_by_name
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
return pnum_by_name.hostport if pnum_by_name
|
|
218
|
+
return pnum_by_destination.hostport if pnum_by_destination
|
|
219
|
+
|
|
220
|
+
# This should NEVER happen.
|
|
221
|
+
raise Errors::SSHPortNotDetected
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
module Vagrant
|
|
2
|
+
class SSH
|
|
3
|
+
# A helper class which wraps around `Net::SSH::Connection::Session`
|
|
4
|
+
# in order to provide basic command error checking while still
|
|
5
|
+
# providing access to the actual session object.
|
|
6
|
+
class Session
|
|
7
|
+
include Util::Retryable
|
|
8
|
+
|
|
9
|
+
attr_reader :session
|
|
10
|
+
attr_reader :env
|
|
11
|
+
|
|
12
|
+
def initialize(session, env)
|
|
13
|
+
@session = session
|
|
14
|
+
@env = env
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Executes a given command and simply returns true/false if the
|
|
18
|
+
# command succeeded or not.
|
|
19
|
+
def test?(command)
|
|
20
|
+
exec!(command) do |ch, type, data|
|
|
21
|
+
return true if type == :exit_status && data == 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Executes a given command on the SSH session using `sudo` and
|
|
28
|
+
# blocks until the command completes. This takes the same parameters
|
|
29
|
+
# as {#exec!}. The only difference is that the command can be an
|
|
30
|
+
# array of commands, which will be placed into the same script.
|
|
31
|
+
#
|
|
32
|
+
# This is different than just calling {#exec!} with `sudo`, since
|
|
33
|
+
# this command is tailor-made to be compliant with older versions
|
|
34
|
+
# of `sudo`.
|
|
35
|
+
def sudo!(commands, options=nil, &block)
|
|
36
|
+
channel = session.open_channel do |ch|
|
|
37
|
+
ch.exec("sudo -H #{env.config.ssh.shell} -l") do |ch2, success|
|
|
38
|
+
# Set the terminal
|
|
39
|
+
ch2.send_data "export TERM=vt100\n"
|
|
40
|
+
|
|
41
|
+
# Output each command as if they were entered on the command line
|
|
42
|
+
[commands].flatten.each do |command|
|
|
43
|
+
ch2.send_data "#{command}\n"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Remember to exit or we'll hang!
|
|
47
|
+
ch2.send_data "exit\n"
|
|
48
|
+
|
|
49
|
+
# Setup the callbacks with our options so we get all the
|
|
50
|
+
# stdout/stderr and error checking goodies
|
|
51
|
+
setup_channel_callbacks(ch2, commands, options, block)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
channel.wait
|
|
56
|
+
channel[:result]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Executes a given command on the SSH session and blocks until
|
|
60
|
+
# the command completes. This is an almost line for line copy of
|
|
61
|
+
# the actual `exec!` implementation, except that this
|
|
62
|
+
# implementation also reports `:exit_status` to the block if given.
|
|
63
|
+
def exec!(commands, options=nil, &block)
|
|
64
|
+
retryable(:tries => 5, :on => [IOError, Net::SSH::Disconnect], :sleep => 1.0) do
|
|
65
|
+
metach = session.open_channel do |ch|
|
|
66
|
+
ch.exec("#{env.config.ssh.shell} -l") do |ch2, success|
|
|
67
|
+
# Set the terminal
|
|
68
|
+
ch2.send_data "export TERM=vt100\n"
|
|
69
|
+
|
|
70
|
+
# Output the commands as if they were entered on the command line
|
|
71
|
+
[commands].flatten.each do |command|
|
|
72
|
+
ch2.send_data "#{command}\n"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Remember to exit
|
|
76
|
+
ch2.send_data "exit\n"
|
|
77
|
+
|
|
78
|
+
# Setup the callbacks
|
|
79
|
+
setup_channel_callbacks(ch2, commands, options, block)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
metach.wait
|
|
84
|
+
metach[:result]
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Sets up the channel callbacks to properly check exit statuses and
|
|
89
|
+
# callback on stdout/stderr.
|
|
90
|
+
def setup_channel_callbacks(channel, command, options, block)
|
|
91
|
+
options = { :error_check => true }.merge(options || {})
|
|
92
|
+
|
|
93
|
+
block ||= Proc.new do |ch, type, data|
|
|
94
|
+
check_exit_status(data, command, options, ch[:result]) if type == :exit_status && options[:error_check]
|
|
95
|
+
|
|
96
|
+
ch[:result] ||= ""
|
|
97
|
+
ch[:result] << data if [:stdout, :stderr].include?(type)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Output stdout data to the block
|
|
101
|
+
channel.on_data do |ch2, data|
|
|
102
|
+
# This clears the screen, we want to filter it out.
|
|
103
|
+
data.gsub!("\e[H", "")
|
|
104
|
+
|
|
105
|
+
block.call(ch2, :stdout, data)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Output stderr data to the block
|
|
109
|
+
channel.on_extended_data do |ch2, type, data|
|
|
110
|
+
block.call(ch2, :stderr, data)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Output exit status information to the block
|
|
114
|
+
channel.on_request("exit-status") do |ch2, data|
|
|
115
|
+
block.call(ch2, :exit_status, data.read_long)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Checks for an erroroneous exit status and raises an exception
|
|
120
|
+
# if so.
|
|
121
|
+
def check_exit_status(exit_status, commands, options=nil, output=nil)
|
|
122
|
+
if exit_status != 0
|
|
123
|
+
output ||= '[no output]'
|
|
124
|
+
options = {
|
|
125
|
+
:_error_class => Errors::VagrantError,
|
|
126
|
+
:_key => :ssh_bad_exit_status,
|
|
127
|
+
:command => [commands].flatten.join("\n"),
|
|
128
|
+
:output => output
|
|
129
|
+
}.merge(options || {})
|
|
130
|
+
|
|
131
|
+
raise options[:_error_class], options
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|