synced_folders/rsync: move rsync logic out to helper

This commit is contained in:
Mitchell Hashimoto 2014-01-13 11:01:50 -08:00
parent 9b383740ba
commit 59218ded68
8 changed files with 219 additions and 138 deletions

View File

@ -324,7 +324,7 @@ module Vagrant
# Returns the state of this machine. The state is queried from the
# backing provider, so it can be any arbitrary symbol.
#
# @return [Symbol]
# @return [MachineState]
def state
result = @provider.state
raise Errors::MachineStateInvalid if !result.is_a?(MachineState)

View File

@ -0,0 +1,38 @@
require 'optparse'
require_relative "../helper"
module VagrantPlugins
module SyncedFolderRSync
module Command
class Rsync < Vagrant.plugin("2", :command)
def self.synopsis
"syncs rsync synced folders to remote machine"
end
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant rsync [vm-name]"
o.separator ""
end
# Parse the options and return if we don't have any target.
argv = parse_options(opts)
return if !argv
# Go through each machine and perform the rsync
error = false
with_target_vms(argv) do |machine|
if !machine.communicate.ready?
machine.ui.error(I18n.t("vagrant.rsync_communicator_not_ready"))
error = true
next
end
end
return error ? 1 : 0
end
end
end
end
end

View File

@ -0,0 +1,66 @@
require "vagrant/util/subprocess"
module VagrantPlugins
module SyncedFolderRSync
# This is a helper that abstracts out the functionality of rsyncing
# folders so that it can be called from anywhere.
class RsyncHelper
def self.rsync_single(machine, ssh_info, 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.
excludes = ['.vagrant/']
excludes += Array(opts[:exclude]).map(&:to_s) if opts[:exclude]
excludes.uniq!
# 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] = machine.env.root_path.to_s
machine.ui.info(I18n.t(
"vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath))
if excludes.length > 1
machine.ui.info(I18n.t(
"vagrant.rsync_folder_excludes", excludes: excludes.inspect))
end
# If we have tasks to do before rsyncing, do those.
if machine.guest.capability?(:rsync_pre)
machine.guest.capability(:rsync_pre, opts)
end
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

View File

@ -9,6 +9,11 @@ module VagrantPlugins
The Rsync synced folder plugin will sync folders via rsync.
EOF
command("rsync", primary: false) do
require_relative "command/rsync"
Command::Rsync
end
synced_folder("rsync", 5) do
require_relative "synced_folder"
SyncedFolder

View File

@ -3,6 +3,8 @@ require "log4r"
require "vagrant/util/subprocess"
require "vagrant/util/which"
require_relative "helper"
module VagrantPlugins
module SyncedFolderRSync
class SyncedFolder < Vagrant.plugin("2", :synced_folder)
@ -33,65 +35,7 @@ module VagrantPlugins
end
folders.each do |id, folder_opts|
rsync_single(machine, ssh_info, folder_opts)
end
end
# rsync_single rsync's a single folder with the given options.
def rsync_single(machine, ssh_info, 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.
excludes = ['.vagrant/']
excludes += Array(opts[:exclude]).map(&:to_s) if opts[:exclude]
excludes.uniq!
# 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] = machine.env.root_path.to_s
machine.ui.info(I18n.t(
"vagrant.rsync_folder", guestpath: guestpath, hostpath: hostpath))
if excludes.length > 1
machine.ui.info(I18n.t(
"vagrant.rsync_folder_excludes", excludes: excludes.inspect))
end
# If we have tasks to do before rsyncing, do those.
if machine.guest.capability?(:rsync_pre)
machine.guest.capability(:rsync_pre, opts)
end
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
RsyncHelper.rsync_single(machine, ssh_info, folder_opts)
end
end
end

View File

@ -85,6 +85,9 @@ en:
%{names}
provisioner_cleanup: |-
Running cleanup tasks for '%{name}' provisioner...
rsync_communicator_not_ready: |-
The machine is reporting that it is not ready for rsync to
communiate with it. Verify that this machine is properly running.
rsync_folder: |-
Rsyncing folder: %{hostpath} => %{guestpath}
rsync_folder_excludes: " - Exclude: %{excludes}"

View File

@ -0,0 +1,100 @@
require_relative "../../../base"
require Vagrant.source_root.join("plugins/synced_folders/rsync/helper")
describe VagrantPlugins::SyncedFolderRSync::RsyncHelper 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(:guest) { double("guest") }
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
subject { described_class }
before do
machine.stub(guest: guest)
end
describe "#rsync_single" do
let(:result) { Vagrant::Util::Subprocess::Result.new(0, "", "") }
let(:ssh_info) {{
private_key_path: [],
}}
let(:opts) { {} }
let(:ui) { machine.ui }
before do
Vagrant::Util::Subprocess.stub(execute: result)
guest.stub(capability?: false)
end
it "doesn't raise an error if it succeeds" do
subject.rsync_single(machine, ssh_info, 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(machine, ssh_info, 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(machine.env.root_path.to_s)
end
subject.rsync_single(machine, ssh_info, opts)
end
it "executes the rsync_pre capability first if it exists" do
guest.should_receive(:capability?).with(:rsync_pre).and_return(true)
guest.should_receive(:capability).with(:rsync_pre, opts).ordered
Vagrant::Util::Subprocess.should_receive(:execute).ordered.and_return(result)
subject.rsync_single(machine, ssh_info, opts)
end
context "excluding files" do
it "excludes files if given as a string" do
opts[:exclude] = "foo"
Vagrant::Util::Subprocess.should_receive(:execute).with do |*args|
index = args.find_index("foo")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
end
subject.rsync_single(machine, ssh_info, opts)
end
it "excludes multiple files" do
opts[:exclude] = ["foo", "bar"]
Vagrant::Util::Subprocess.should_receive(:execute).with do |*args|
index = args.find_index("foo")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
index = args.find_index("bar")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
end
subject.rsync_single(machine, ssh_info, opts)
end
end
end
end

View File

@ -16,6 +16,8 @@ describe VagrantPlugins::SyncedFolderRSync::SyncedFolder do
let(:host) { double("host") }
let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) }
let(:helper_class) { VagrantPlugins::SyncedFolderRSync::RsyncHelper }
before do
machine.env.stub(host: host)
machine.stub(guest: guest)
@ -55,7 +57,7 @@ describe VagrantPlugins::SyncedFolderRSync::SyncedFolder do
]
folders.each do |_, opts|
subject.should_receive(:rsync_single).
helper_class.should_receive(:rsync_single).
with(machine, ssh_info, opts).
ordered
end
@ -63,81 +65,4 @@ describe VagrantPlugins::SyncedFolderRSync::SyncedFolder do
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(:ui) { machine.ui }
before do
Vagrant::Util::Subprocess.stub(execute: result)
guest.stub(capability?: false)
end
it "doesn't raise an error if it succeeds" do
subject.rsync_single(machine, ssh_info, 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(machine, ssh_info, 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(machine.env.root_path.to_s)
end
subject.rsync_single(machine, ssh_info, opts)
end
it "executes the rsync_pre capability first if it exists" do
guest.should_receive(:capability?).with(:rsync_pre).and_return(true)
guest.should_receive(:capability).with(:rsync_pre, opts).ordered
Vagrant::Util::Subprocess.should_receive(:execute).ordered.and_return(result)
subject.rsync_single(machine, ssh_info, opts)
end
context "excluding files" do
it "excludes files if given as a string" do
opts[:exclude] = "foo"
Vagrant::Util::Subprocess.should_receive(:execute).with do |*args|
index = args.find_index("foo")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
end
subject.rsync_single(machine, ssh_info, opts)
end
it "excludes multiple files" do
opts[:exclude] = ["foo", "bar"]
Vagrant::Util::Subprocess.should_receive(:execute).with do |*args|
index = args.find_index("foo")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
index = args.find_index("bar")
expect(index).to be > 0
expect(args[index-1]).to eql("--exclude")
end
subject.rsync_single(machine, ssh_info, opts)
end
end
end
end