Merge pull request #2820 from mitchellh/f-rsync-listen
`rsync-auto` command
This commit is contained in:
commit
8d771de9ba
|
@ -0,0 +1,97 @@
|
|||
require "log4r"
|
||||
require 'optparse'
|
||||
|
||||
require "listen"
|
||||
|
||||
require "vagrant/action/builtin/mixin_synced_folders"
|
||||
|
||||
require_relative "../helper"
|
||||
|
||||
module VagrantPlugins
|
||||
module SyncedFolderRSync
|
||||
module Command
|
||||
class RsyncAuto < Vagrant.plugin("2", :command)
|
||||
include Vagrant::Action::Builtin::MixinSyncedFolders
|
||||
|
||||
def self.synopsis
|
||||
"syncs rsync synced folders automatically when files change"
|
||||
end
|
||||
|
||||
def execute
|
||||
@logger = Log4r::Logger.new("vagrant::commands::rsync-auto")
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant rsync-auto [vm-name]"
|
||||
o.separator ""
|
||||
end
|
||||
|
||||
# Parse the options and return if we don't have any target.
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
# Build up the paths that we need to listen to.
|
||||
paths = {}
|
||||
with_target_vms(argv) do |machine|
|
||||
folders = synced_folders(machine)[:rsync]
|
||||
next if !folders || folders.empty?
|
||||
|
||||
folders.each do |id, folder_opts|
|
||||
hostpath = folder_opts[:hostpath]
|
||||
hostpath = File.expand_path(hostpath, machine.env.root_path)
|
||||
paths[hostpath] ||= []
|
||||
paths[hostpath] << {
|
||||
id: id,
|
||||
machine: machine,
|
||||
opts: folder_opts,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@logger.info("Listening to paths: #{paths.keys.sort.inspect}")
|
||||
@logger.info("Listening via: #{Listen::Adapter.select.inspect}")
|
||||
callback = method(:callback).to_proc.curry[paths]
|
||||
listener = Listen.to(*paths.keys, &callback)
|
||||
listener.start
|
||||
listener.thread.join
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
# This is the callback that is called when any changes happen
|
||||
def callback(paths, modified, added, removed)
|
||||
@logger.debug("File change callback called!")
|
||||
@logger.debug(" - Modified: #{modified.inspect}")
|
||||
@logger.debug(" - Added: #{added.inspect}")
|
||||
@logger.debug(" - Removed: #{removed.inspect}")
|
||||
|
||||
tosync = []
|
||||
paths.each do |hostpath, folders|
|
||||
# Find out if this path should be synced
|
||||
found = catch(:done) do
|
||||
[modified, added, removed].each do |changed|
|
||||
changed.each do |listenpath|
|
||||
throw :done, true if listenpath.start_with?(hostpath)
|
||||
end
|
||||
end
|
||||
|
||||
# Make sure to return false if all else fails so that we
|
||||
# don't sync to this machine.
|
||||
false
|
||||
end
|
||||
|
||||
# If it should be synced, store it for later
|
||||
tosync << folders if found
|
||||
end
|
||||
|
||||
# Sync all the folders that need to be synced
|
||||
tosync.each do |folders|
|
||||
folders.each do |opts|
|
||||
ssh_info = opts[:machine].ssh_info
|
||||
RsyncHelper.rsync_single(opts[:machine], ssh_info, opts[:opts])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,6 +14,11 @@ module VagrantPlugins
|
|||
Command::Rsync
|
||||
end
|
||||
|
||||
command("rsync-auto", primary: false) do
|
||||
require_relative "command/rsync_auto"
|
||||
Command::RsyncAuto
|
||||
end
|
||||
|
||||
synced_folder("rsync", 5) do
|
||||
require_relative "synced_folder"
|
||||
SyncedFolder
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
require_relative "../../../../base"
|
||||
|
||||
require Vagrant.source_root.join("plugins/synced_folders/rsync/command/rsync_auto")
|
||||
|
||||
describe VagrantPlugins::SyncedFolderRSync::Command::RsyncAuto do
|
||||
include_context "unit"
|
||||
|
||||
let(:argv) { [] }
|
||||
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(:synced_folders) { {} }
|
||||
|
||||
let(:helper_class) { VagrantPlugins::SyncedFolderRSync::RsyncHelper }
|
||||
|
||||
subject do
|
||||
described_class.new(argv, iso_env).tap do |s|
|
||||
s.stub(synced_folders: synced_folders)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#callback" do
|
||||
let(:paths) { {} }
|
||||
let(:ssh_info) {{}}
|
||||
|
||||
def machine_stub(name)
|
||||
double(name).tap do |m|
|
||||
m.stub(ssh_info: ssh_info)
|
||||
end
|
||||
end
|
||||
|
||||
it "syncs modified folders to the proper path" do
|
||||
paths["/foo"] = [
|
||||
{ machine: machine_stub("m1"), opts: double("opts_m1") },
|
||||
{ machine: machine_stub("m2"), opts: double("opts_m2") },
|
||||
]
|
||||
paths["/bar"] = [
|
||||
{ machine: machine_stub("m3"), opts: double("opts_m3") },
|
||||
]
|
||||
|
||||
paths["/foo"].each do |data|
|
||||
helper_class.should_receive(:rsync_single).
|
||||
with(data[:machine], data[:machine].ssh_info, data[:opts]).
|
||||
once
|
||||
end
|
||||
|
||||
m = ["/foo/bar"]
|
||||
a = []
|
||||
r = []
|
||||
subject.callback(paths, m, a, r)
|
||||
end
|
||||
|
||||
it "syncs added folders to the proper path" do
|
||||
paths["/foo"] = [
|
||||
{ machine: machine_stub("m1"), opts: double("opts_m1") },
|
||||
{ machine: machine_stub("m2"), opts: double("opts_m2") },
|
||||
]
|
||||
paths["/bar"] = [
|
||||
{ machine: machine_stub("m3"), opts: double("opts_m3") },
|
||||
]
|
||||
|
||||
paths["/foo"].each do |data|
|
||||
helper_class.should_receive(:rsync_single).
|
||||
with(data[:machine], data[:machine].ssh_info, data[:opts]).
|
||||
once
|
||||
end
|
||||
|
||||
m = []
|
||||
a = ["/foo/bar"]
|
||||
r = []
|
||||
subject.callback(paths, m, a, r)
|
||||
end
|
||||
|
||||
it "syncs removed folders to the proper path" do
|
||||
paths["/foo"] = [
|
||||
{ machine: machine_stub("m1"), opts: double("opts_m1") },
|
||||
{ machine: machine_stub("m2"), opts: double("opts_m2") },
|
||||
]
|
||||
paths["/bar"] = [
|
||||
{ machine: machine_stub("m3"), opts: double("opts_m3") },
|
||||
]
|
||||
|
||||
paths["/foo"].each do |data|
|
||||
helper_class.should_receive(:rsync_single).
|
||||
with(data[:machine], data[:machine].ssh_info, data[:opts]).
|
||||
once
|
||||
end
|
||||
|
||||
m = []
|
||||
a = []
|
||||
r = ["/foo/bar"]
|
||||
subject.callback(paths, m, a, r)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -18,9 +18,12 @@ Gem::Specification.new do |s|
|
|||
s.add_dependency "childprocess", "~> 0.3.7"
|
||||
s.add_dependency "erubis", "~> 2.7.0"
|
||||
s.add_dependency "i18n", "~> 0.6.0"
|
||||
s.add_dependency "listen", "~> 2.4.0"
|
||||
s.add_dependency "log4r", "~> 1.1.9", "< 1.1.11"
|
||||
s.add_dependency "net-ssh", ">= 2.6.6", "< 2.8.0"
|
||||
s.add_dependency "net-scp", "~> 1.1.0"
|
||||
s.add_dependency "rb-kqueue", "~> 0.2.0"
|
||||
s.add_dependency "wdm", "~> 0.1.0"
|
||||
|
||||
s.add_development_dependency "rake"
|
||||
s.add_development_dependency "contest", ">= 0.1.2"
|
||||
|
|
|
@ -22,3 +22,4 @@ The list of non-primary commands is below. Click on any command to learn
|
|||
more about it.
|
||||
|
||||
* [rsync](/v2/cli/rsync.html)
|
||||
* [rsync-auto](/v2/cli/rsync-auto.html)
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
page_title: "vagrant rsync-auto - Command-Line Interface"
|
||||
sidebar_current: "cli-rsyncauto"
|
||||
---
|
||||
|
||||
# rsync-auto
|
||||
|
||||
**Command: `vagrant rsync-auto`**
|
||||
|
||||
This command watches all local directories of anj
|
||||
[rsync synced folders](/v2/synced-folders/rsync.html) and automatically
|
||||
initiates an rsync transfer when changes are detected. This command does
|
||||
not exit until an interrupt is received.
|
||||
|
||||
The change detection is optimized to use platform-specific APIs to listen
|
||||
for filesystem changes, and does not simply poll the directory.
|
||||
|
||||
## Machine State Changes
|
||||
|
||||
The `rsync-auto` command does not currently handle machine state changes
|
||||
gracefully. For example, if you start the `rsync-auto` command, then
|
||||
halt the guest machine, then make changes to some files, then boot it
|
||||
back up, `rsync-auto` will not attempt to resync.
|
||||
|
||||
To ensure that the command works properly, you should start `rsync-auto`
|
||||
only when the machine is running, and shut it down before any machine
|
||||
state changes.
|
||||
|
||||
You can always force a resync with the [rsync](/v2/cli/rsync.html) command.
|
||||
|
||||
## Vagrantfile Changes
|
||||
|
||||
If you change or move your Vagrantfile, the `rsync-auto` command will have
|
||||
to be restarted. For example, if you add synced folders to the Vagrantfile,
|
||||
or move the directory that contains the Vagrantfile, the `rsync-auto`
|
||||
command will either not pick up the changes or may begin experiencing
|
||||
strange behavior.
|
||||
|
||||
Before making any such changes, it is recommended that you turn off
|
||||
`rsync-auto`, then restart it afterwards.
|
Loading…
Reference in New Issue