synced_folders/rsync: Initial commit working
This commit is contained in:
parent
213000fd3d
commit
38fbbb6c56
|
@ -71,7 +71,7 @@ module Vagrant
|
||||||
raise "Internal error. Report this as a bug. Invalid: #{data[:type]}"
|
raise "Internal error. Report this as a bug. Invalid: #{data[:type]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if !impl_class[0].new.usable?(machine)
|
if !impl_class[0].new.usable?(machine, true)
|
||||||
# Verify that explicitly defined shared folder types are
|
# Verify that explicitly defined shared folder types are
|
||||||
# actually usable.
|
# actually usable.
|
||||||
raise Errors::SyncedFolderUnusable, type: data[:type].to_s
|
raise Errors::SyncedFolderUnusable, type: data[:type].to_s
|
||||||
|
|
|
@ -420,6 +420,14 @@ module Vagrant
|
||||||
error_key(:plugin_state_file_not_parsable)
|
error_key(:plugin_state_file_not_parsable)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class RSyncError < VagrantError
|
||||||
|
error_key(:rsync_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
class RSyncNotFound < VagrantError
|
||||||
|
error_key(:rsync_not_found)
|
||||||
|
end
|
||||||
|
|
||||||
class SCPPermissionDenied < VagrantError
|
class SCPPermissionDenied < VagrantError
|
||||||
error_key(:scp_permission_denied)
|
error_key(:scp_permission_denied)
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,11 @@ module Vagrant
|
||||||
# if this implementation can be used for this machine. This should
|
# if this implementation can be used for this machine. This should
|
||||||
# return true or false.
|
# return true or false.
|
||||||
#
|
#
|
||||||
|
# @param [Machine] machine
|
||||||
|
# @param [Boolean] raise_error If true, should raise an exception
|
||||||
|
# if it isn't usable.
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
def usable?(machine)
|
def usable?(machine, raise_error=false)
|
||||||
end
|
end
|
||||||
|
|
||||||
# This is called before the machine is booted, allowing the
|
# This is called before the machine is booted, allowing the
|
||||||
|
|
|
@ -24,7 +24,7 @@ module VagrantPlugins
|
||||||
@logger = Log4r::Logger.new("vagrant::synced_folders::nfs")
|
@logger = Log4r::Logger.new("vagrant::synced_folders::nfs")
|
||||||
end
|
end
|
||||||
|
|
||||||
def usable?(machine)
|
def usable?(machine, raise_error=false)
|
||||||
# NFS is always available
|
# NFS is always available
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
require "vagrant"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module SyncedFolderRSync
|
||||||
|
# This plugin implements synced folders via rsync.
|
||||||
|
class Plugin < Vagrant.plugin("2")
|
||||||
|
name "RSync synced folders"
|
||||||
|
description <<-EOF
|
||||||
|
The Rsync synced folder plugin will sync folders via rsync.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
synced_folder("rsync", 5) do
|
||||||
|
require_relative "synced_folder"
|
||||||
|
SyncedFolder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,86 @@
|
||||||
|
require "log4r"
|
||||||
|
|
||||||
|
require "vagrant/util/subprocess"
|
||||||
|
require "vagrant/util/which"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module SyncedFolderRSync
|
||||||
|
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
|
||||||
|
include Vagrant::Util
|
||||||
|
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
|
||||||
|
@logger = Log4r::Logger.new("vagrant::synced_folders::rsync")
|
||||||
|
end
|
||||||
|
|
||||||
|
def usable?(machine, raise_error=false)
|
||||||
|
rsync_path = Which.which("rsync")
|
||||||
|
return true if rsync_path
|
||||||
|
return false if !raise_error
|
||||||
|
raise Vagrant::Errors::RSyncNotFound
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare(machine, folders, opts)
|
||||||
|
# Nothing is necessary to do before VM boot.
|
||||||
|
end
|
||||||
|
|
||||||
|
def enable(machine, folders, opts)
|
||||||
|
rootdir = machine.env.root_path.to_s
|
||||||
|
ssh_info = machine.ssh_info
|
||||||
|
|
||||||
|
folders.each do |id, folder_opts|
|
||||||
|
rsync_single(ssh_info, rootdir, machine.ui, folder_opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# rsync_single rsync's a single folder with the given options.
|
||||||
|
def rsync_single(ssh_info, rootdir, ui, opts)
|
||||||
|
# Folder info
|
||||||
|
guestpath = opts[:guestpath]
|
||||||
|
hostpath = opts[:hostpath]
|
||||||
|
|
||||||
|
# Connection information
|
||||||
|
username = ssh_info[:username]
|
||||||
|
host = ssh_info[:host]
|
||||||
|
rsh = [
|
||||||
|
"ssh -p #{ssh_info[:port]} -o StrictHostKeyChecking=no",
|
||||||
|
ssh_info[:private_key_path].map { |p| "-i '#{p}'" },
|
||||||
|
].flatten.join(" ")
|
||||||
|
|
||||||
|
# Exclude some files by default, and any that might be configured
|
||||||
|
# by the user.
|
||||||
|
# TODO(mitchellh): allow the user to configure it
|
||||||
|
excludes = ['.vagrant/']
|
||||||
|
|
||||||
|
# Build up the actual command to execute
|
||||||
|
command = [
|
||||||
|
"rsync",
|
||||||
|
"--verbose",
|
||||||
|
"--archive",
|
||||||
|
"-z",
|
||||||
|
excludes.map { |e| ["--exclude", e] },
|
||||||
|
"-e", rsh,
|
||||||
|
hostpath,
|
||||||
|
"#{username}@#{host}:#{guestpath}"
|
||||||
|
].flatten
|
||||||
|
|
||||||
|
command_opts = {}
|
||||||
|
|
||||||
|
# The working directory should be the root path
|
||||||
|
command_opts[:workdir] = rootdir
|
||||||
|
|
||||||
|
ui.info(I18n.t(
|
||||||
|
"vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath))
|
||||||
|
r = Vagrant::Util::Subprocess.execute(*(command + [command_opts]))
|
||||||
|
if r.exit_code != 0
|
||||||
|
raise Vagrant::Errors::RSyncError,
|
||||||
|
command: command.join(" "),
|
||||||
|
guestpath: guestpath,
|
||||||
|
hostpath: hostpath,
|
||||||
|
stderr: r.stderr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -80,6 +80,8 @@ en:
|
||||||
%{names}
|
%{names}
|
||||||
provisioner_cleanup: |-
|
provisioner_cleanup: |-
|
||||||
Running cleanup tasks for '%{name}' provisioner...
|
Running cleanup tasks for '%{name}' provisioner...
|
||||||
|
rsync_folder: |-
|
||||||
|
Rsyncing folder: %{hostpath} => %{guestpath}
|
||||||
ssh_exec_password: |-
|
ssh_exec_password: |-
|
||||||
The machine you're attempting to SSH into is configured to use
|
The machine you're attempting to SSH into is configured to use
|
||||||
password-based authentication. Vagrant can't script entering the
|
password-based authentication. Vagrant can't script entering the
|
||||||
|
@ -525,6 +527,17 @@ en:
|
||||||
provisioner_flag_invalid: |-
|
provisioner_flag_invalid: |-
|
||||||
'%{name}' is not a known provisioner. Please specify a valid
|
'%{name}' is not a known provisioner. Please specify a valid
|
||||||
provisioner.
|
provisioner.
|
||||||
|
rsync_error: |-
|
||||||
|
There was an error when attempting to rsync a synced folder.
|
||||||
|
Please inspect the error message below for more info.
|
||||||
|
|
||||||
|
Host path: %{hostpath}
|
||||||
|
Guest path: %{guestpath}
|
||||||
|
Command: %{command}
|
||||||
|
Error: %{stderr}
|
||||||
|
rsync_not_found: |-
|
||||||
|
"rsync" could not be found on your PATH. Make sure that rsync
|
||||||
|
is properly installed on your system and available on the PATH.
|
||||||
scp_permission_denied: |-
|
scp_permission_denied: |-
|
||||||
Failed to upload a file to the guest VM via SCP due to a permissions
|
Failed to upload a file to the guest VM via SCP due to a permissions
|
||||||
error. This is normally because the user running Vagrant doesn't have
|
error. This is normally because the user running Vagrant doesn't have
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
require_relative "../../../base"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/synced_folders/rsync/synced_folder")
|
||||||
|
|
||||||
|
describe VagrantPlugins::SyncedFolderRSync::SyncedFolder do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
let(:iso_env) do
|
||||||
|
# We have to create a Vagrantfile so there is a root path
|
||||||
|
env = isolated_environment
|
||||||
|
env.vagrantfile("")
|
||||||
|
env.create_vagrant_env
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:host) { double("host") }
|
||||||
|
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.env.stub(host: host)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#usable?" do
|
||||||
|
it "is usable if rsync can be found" do
|
||||||
|
Vagrant::Util::Which.should_receive(:which).with("rsync").and_return(true)
|
||||||
|
expect(subject.usable?(machine)).to be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is not usable if rsync cant be found" do
|
||||||
|
Vagrant::Util::Which.should_receive(:which).with("rsync").and_return(false)
|
||||||
|
expect(subject.usable?(machine)).to be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an exception if asked to" do
|
||||||
|
Vagrant::Util::Which.should_receive(:which).with("rsync").and_return(false)
|
||||||
|
expect { subject.usable?(machine, true) }.
|
||||||
|
to raise_error(Vagrant::Errors::RSyncNotFound)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#enable" do
|
||||||
|
let(:ssh_info) { Object.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
machine.stub(ssh_info: ssh_info)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "rsyncs each folder" do
|
||||||
|
folders = [
|
||||||
|
[:one, {}],
|
||||||
|
[:two, {}],
|
||||||
|
]
|
||||||
|
|
||||||
|
folders.each do |_, opts|
|
||||||
|
subject.should_receive(:rsync_single).
|
||||||
|
with(ssh_info, machine.env.root_path.to_s, machine.ui, opts).
|
||||||
|
ordered
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.enable(machine, folders, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#rsync_single" do
|
||||||
|
let(:result) { Vagrant::Util::Subprocess::Result.new(0, "", "") }
|
||||||
|
|
||||||
|
let(:ssh_info) {{
|
||||||
|
private_key_path: [],
|
||||||
|
}}
|
||||||
|
let(:opts) { {} }
|
||||||
|
let(:root_path) { "foo" }
|
||||||
|
let(:ui) { machine.ui }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Vagrant::Util::Subprocess.stub(execute: result)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't raise an error if it succeeds" do
|
||||||
|
subject.rsync_single(ssh_info, root_path, ui, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "raises an error if the exit code is non-zero" do
|
||||||
|
Vagrant::Util::Subprocess.stub(
|
||||||
|
execute: Vagrant::Util::Subprocess::Result.new(1, "", ""))
|
||||||
|
|
||||||
|
expect {subject.rsync_single(ssh_info, root_path, ui, opts) }.
|
||||||
|
to raise_error(Vagrant::Errors::RSyncError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "executes within the root path" do
|
||||||
|
Vagrant::Util::Subprocess.should_receive(:execute).with do |*args|
|
||||||
|
expect(args.last).to be_kind_of(Hash)
|
||||||
|
|
||||||
|
opts = args.last
|
||||||
|
expect(opts[:workdir]).to eql(root_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject.rsync_single(ssh_info, root_path, ui, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,8 @@ shared_context "synced folder actions" do
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
|
|
||||||
define_method(:usable?) do |machine|
|
define_method(:usable?) do |machine, raise_error=false|
|
||||||
|
raise "#{name}: usable" if raise_error && !usable
|
||||||
usable
|
usable
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue