vagrant-mirror 0.1.0.alpha
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 +17 -0
- data/.rspec +3 -0
- data/.travis.yml +3 -0
- data/CONTRIBUTING.md +43 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +85 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +153 -0
- data/Rakefile +29 -0
- data/lib/vagrant-mirror/command.rb +71 -0
- data/lib/vagrant-mirror/config.rb +73 -0
- data/lib/vagrant-mirror/errors.rb +42 -0
- data/lib/vagrant-mirror/listener/host.rb +42 -0
- data/lib/vagrant-mirror/middleware/base.rb +65 -0
- data/lib/vagrant-mirror/middleware/mirror.rb +108 -0
- data/lib/vagrant-mirror/middleware/sync.rb +92 -0
- data/lib/vagrant-mirror/rsync.rb +91 -0
- data/lib/vagrant-mirror/sync/all.rb +60 -0
- data/lib/vagrant-mirror/sync/base.rb +98 -0
- data/lib/vagrant-mirror/sync/changes.rb +61 -0
- data/lib/vagrant-mirror/version.rb +5 -0
- data/lib/vagrant-mirror.rb +34 -0
- data/lib/vagrant_init.rb +4 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/vagrant-mirror/command_spec.rb +91 -0
- data/spec/vagrant-mirror/config_spec.rb +161 -0
- data/spec/vagrant-mirror/listener/host_spec.rb +96 -0
- data/spec/vagrant-mirror/middleware/mirror_spec.rb +188 -0
- data/spec/vagrant-mirror/middleware/sync_spec.rb +152 -0
- data/spec/vagrant-mirror/rsync_spec.rb +75 -0
- data/spec/vagrant-mirror_spec.rb +5 -0
- data/vagrant-mirror.gemspec +27 -0
- metadata +153 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
describe Vagrant::Mirror::Config do
|
2
|
+
|
3
|
+
context "with no configuration" do
|
4
|
+
|
5
|
+
it "returns an empty array of mirrored folders" do
|
6
|
+
subject.folders.should eq([])
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when a mirrored folder is added with no options" do
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
subject.folder "foo", "/var/path"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "adds it to the list of folders" do
|
18
|
+
subject.folders.count.should eq 1
|
19
|
+
subject.folders[0][:name].should eq 'foo'
|
20
|
+
subject.folders[0][:guest_path].should eq "/var/path"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "does not beep by default" do
|
24
|
+
subject.folders[0][:beep].should be_false
|
25
|
+
end
|
26
|
+
|
27
|
+
it "does not delete by default" do
|
28
|
+
subject.folders[0][:delete].should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "does not exclude anything by default" do
|
32
|
+
subject.folders[0][:exclude].should eq([])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does not make any symlinks by default" do
|
36
|
+
subject.folders[0][:symlinks].should eq([])
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
it "provides a shortcut for mirroring the vagrant root to the guest" do
|
42
|
+
subject.vagrant_root "/var/vagrant"
|
43
|
+
|
44
|
+
subject.folders.count.should eq 1
|
45
|
+
subject.folders[0][:name].should eq 'v-root'
|
46
|
+
subject.folders[0][:guest_path].should eq "/var/vagrant"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can accept a folder with the delete option set" do
|
50
|
+
subject.vagrant_root "/var/vagrant", { :delete => true }
|
51
|
+
|
52
|
+
subject.folders[0][:delete].should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "can accept a folder with the beep option set" do
|
56
|
+
subject.vagrant_root "/var/vagrant", { :beep => true }
|
57
|
+
|
58
|
+
subject.folders[0][:beep].should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it "can accept a folder with exclude patterns set" do
|
62
|
+
subject.vagrant_root "/var/vagrant", { :exclude => ['/docs'] }
|
63
|
+
|
64
|
+
subject.folders[0][:exclude].should eq(['/docs'])
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when adding a folder with excludes and symlinks" do
|
68
|
+
before (:each) do
|
69
|
+
subject.vagrant_root "/var/vagrant", { :exclude => ['/docs'], :symlinks => ['/log'] }
|
70
|
+
end
|
71
|
+
|
72
|
+
it "stores the list of symlinks" do
|
73
|
+
subject.folders[0][:symlinks].should eq(['/log'])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "adds the symlink to the rsync exclude list" do
|
77
|
+
subject.folders[0][:exclude].should eq(['/docs', '/log'])
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when adding multiple folders" do
|
83
|
+
before (:each) do
|
84
|
+
subject.vagrant_root "/var/vagrant", { :exclude => ['/docs'], :symlinks => ['/log'] }
|
85
|
+
subject.folder "foo", "/var/foo", { :exclude => ['/docs2'], :symlinks => ['/log2'] }
|
86
|
+
end
|
87
|
+
|
88
|
+
it "builds a list of folders" do
|
89
|
+
subject.folders.count.should eq(2)
|
90
|
+
subject.folders[0][:name].should eq('v-root')
|
91
|
+
subject.folders[1][:name].should eq('foo')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when validating configuration" do
|
96
|
+
|
97
|
+
before :each do
|
98
|
+
@env = Vagrant::Environment.new
|
99
|
+
@errors = Vagrant::Config::ErrorRecorder.new
|
100
|
+
end
|
101
|
+
|
102
|
+
it "rejects folders with an empty shared folder name" do
|
103
|
+
subject.folder "", "/var/path"
|
104
|
+
subject.validate(@env, @errors)
|
105
|
+
|
106
|
+
@errors.errors.empty?.should be_false
|
107
|
+
end
|
108
|
+
|
109
|
+
it "rejects folders with an empty guest path" do
|
110
|
+
subject.folder "foo", ""
|
111
|
+
subject.validate(@env, @errors)
|
112
|
+
|
113
|
+
@errors.errors.empty?.should be_false
|
114
|
+
end
|
115
|
+
|
116
|
+
it "rejects folders which are not already a vagrant shared folder" do
|
117
|
+
pending
|
118
|
+
end
|
119
|
+
|
120
|
+
it "rejects folders with nil paths" do
|
121
|
+
subject.folder nil, nil
|
122
|
+
subject.validate(@env, @errors)
|
123
|
+
@errors.errors.count.should eq 2
|
124
|
+
end
|
125
|
+
|
126
|
+
it "rejects unknown options" do
|
127
|
+
subject.vagrant_root "/guest/path", { :foo => :bar }
|
128
|
+
subject.validate(@env, @errors)
|
129
|
+
|
130
|
+
@errors.errors.empty?.should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it "validates with valid options" do
|
134
|
+
subject.vagrant_root "/var/guest", { :symlinks => [ 'logs'], :exclude => ['exclude'], :delete => true, :beep => true }
|
135
|
+
subject.validate(@env, @errors)
|
136
|
+
|
137
|
+
@errors.errors.empty?.should be_true
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when merging configuration" do
|
143
|
+
|
144
|
+
it "combines the folders from each configuration source" do
|
145
|
+
subject.folder "low", "/low-level"
|
146
|
+
next_config = Vagrant::Mirror::Config.new
|
147
|
+
next_config.folder "top", "/top-level"
|
148
|
+
merged = subject.merge(next_config)
|
149
|
+
|
150
|
+
merged.should be_a Vagrant::Mirror::Config
|
151
|
+
merged.folders.should have(2).items
|
152
|
+
|
153
|
+
expected_result = [
|
154
|
+
subject.folders[0],
|
155
|
+
next_config.folders[0]
|
156
|
+
]
|
157
|
+
|
158
|
+
merged.folders.should eq expected_result
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
describe Vagrant::Mirror::Listener::Host do
|
2
|
+
|
3
|
+
let (:queue) { double("Queue").as_null_object }
|
4
|
+
|
5
|
+
subject { Vagrant::Mirror::Listener::Host.new('c:/host', queue) }
|
6
|
+
|
7
|
+
before (:each) do
|
8
|
+
Listen.stub(:to).as_null_object
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#listen!" do
|
12
|
+
it "listens to the host directory" do
|
13
|
+
Listen.should_receive(:to).with('c:/host', anything())
|
14
|
+
|
15
|
+
subject.listen!
|
16
|
+
end
|
17
|
+
|
18
|
+
it "requests relative paths" do
|
19
|
+
Listen.should_receive(:to)
|
20
|
+
.with(anything(), hash_including( :relative_paths => true ))
|
21
|
+
|
22
|
+
subject.listen!
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when changes received" do
|
26
|
+
before(:each) do
|
27
|
+
Listen.stub(:to)
|
28
|
+
.and_yield(['modified'],[],[])
|
29
|
+
.and_yield([],['added'],[])
|
30
|
+
.and_yield([],[],['removed'])
|
31
|
+
.and_yield(['modified1'],['added1'],['removed1'])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "pushes added files onto the queue" do
|
35
|
+
queue.should_receive(:"<<")
|
36
|
+
.with(hash_including(:added => ['added']))
|
37
|
+
|
38
|
+
subject.listen!
|
39
|
+
end
|
40
|
+
|
41
|
+
it "pushes modified files onto the queue" do
|
42
|
+
queue.should_receive(:"<<")
|
43
|
+
.with(hash_including(:modified => ['modified']))
|
44
|
+
|
45
|
+
subject.listen!
|
46
|
+
end
|
47
|
+
|
48
|
+
it "pushes removed files onto the queue" do
|
49
|
+
queue.should_receive(:"<<")
|
50
|
+
.with(hash_including(:removed => ['removed']))
|
51
|
+
|
52
|
+
subject.listen!
|
53
|
+
end
|
54
|
+
|
55
|
+
it "handles simultaneous changes" do
|
56
|
+
queue.should_receive(:"<<")
|
57
|
+
.with(hash_including({
|
58
|
+
:added => ['added1'],
|
59
|
+
:modified => ['modified1'],
|
60
|
+
:removed => ['removed1']
|
61
|
+
}))
|
62
|
+
|
63
|
+
subject.listen!
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with a close signal" do
|
69
|
+
it "stops listening"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#listen" do
|
74
|
+
let (:thread) { double("Thread") }
|
75
|
+
|
76
|
+
before (:each) do
|
77
|
+
Thread.stub(:new).and_yield.and_return(thread)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "starts a new thread" do
|
81
|
+
Thread.should_receive(:new)
|
82
|
+
|
83
|
+
subject.listen
|
84
|
+
end
|
85
|
+
|
86
|
+
it "runs listen! in the new thread" do
|
87
|
+
Listen.should_receive(:to)
|
88
|
+
|
89
|
+
subject.listen
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns the thread" do
|
93
|
+
subject.listen.should eq thread
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
describe Vagrant::Mirror::Middleware::Mirror do
|
2
|
+
# Shared mocks
|
3
|
+
let (:env) { Vagrant::Action::Environment.new }
|
4
|
+
let (:vm) { double("Vagrant::VM").as_null_object }
|
5
|
+
let (:ui) { double("Vagrant::UI::Interface").as_null_object }
|
6
|
+
let (:app) { double("Object").as_null_object }
|
7
|
+
let (:channel) { double("Vagrant::Communication::SSH").as_null_object }
|
8
|
+
let (:config) { double("Object").as_null_object }
|
9
|
+
let (:configmirror) { Vagrant::Mirror::Config.new }
|
10
|
+
let (:configvm) { Vagrant::Config::VMConfig.new }
|
11
|
+
let (:queue) { double("Queue").as_null_object }
|
12
|
+
let (:guard) { double("Vagrant::Mirror::Listener::Host").as_null_object }
|
13
|
+
let (:rsync) { double("Vagrant::Mirror::Rsync").as_null_object }
|
14
|
+
|
15
|
+
# Set basic stubs for shared mocks
|
16
|
+
before (:each) do
|
17
|
+
env[:vm] = vm
|
18
|
+
env[:ui] = ui
|
19
|
+
env[:root_path] = Dir.pwd
|
20
|
+
|
21
|
+
vm.stub(:config).and_return config
|
22
|
+
config.stub(:mirror).and_return configmirror
|
23
|
+
config.stub(:vm).and_return configvm
|
24
|
+
|
25
|
+
vm.stub(:channel).and_return channel
|
26
|
+
|
27
|
+
Vagrant::Mirror::Rsync.stub(:new).and_return rsync
|
28
|
+
|
29
|
+
app.stub(:call)
|
30
|
+
|
31
|
+
Vagrant::Mirror::Listener::Host.stub(:new).and_return(guard)
|
32
|
+
Queue.stub(:new).and_return(queue)
|
33
|
+
queue.stub(:pop).and_return({ :quit => true})
|
34
|
+
end
|
35
|
+
|
36
|
+
subject { Vagrant::Mirror::Middleware::Mirror.new(app, env) }
|
37
|
+
|
38
|
+
describe "#call" do
|
39
|
+
shared_examples "chained mirror middleware" do
|
40
|
+
it "calls the next middleware" do
|
41
|
+
app.should_receive(:call).with(env)
|
42
|
+
|
43
|
+
subject.call(env)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with no mirrored folders" do
|
48
|
+
before (:each) do
|
49
|
+
configmirror.stub(:folders).and_return([])
|
50
|
+
end
|
51
|
+
|
52
|
+
it_behaves_like "chained mirror middleware"
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with a mirrored folder" do
|
56
|
+
|
57
|
+
before (:each) do
|
58
|
+
config.mirror.vagrant_root '/var/guest'
|
59
|
+
config.vm.share_folder("v-root", "/vagrant", ".")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "logs the start of mirroring" do
|
63
|
+
ui.should_receive(:info).with("Beginning directory mirroring")
|
64
|
+
|
65
|
+
subject.call(env)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "creates a Guard listener on the host path" do
|
69
|
+
Vagrant::Mirror::Listener::Host.should_receive(:new)
|
70
|
+
.with(env[:root_path], queue)
|
71
|
+
|
72
|
+
subject.call(env)
|
73
|
+
end
|
74
|
+
|
75
|
+
context "creates an rsync class for the folder pair" do
|
76
|
+
it "creates an rsync class for the folder pair" do
|
77
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
78
|
+
.and_return(rsync)
|
79
|
+
|
80
|
+
subject.call(env)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "passes the vm to rsync" do
|
84
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
85
|
+
.with(vm, anything, anything, anything)
|
86
|
+
.and_return(rsync)
|
87
|
+
|
88
|
+
subject.call(env)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "passes the guest shared folder path for the folder pair to rsync" do
|
92
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
93
|
+
.with(anything, "/vagrant", anything, anything)
|
94
|
+
.and_return(rsync)
|
95
|
+
|
96
|
+
subject.call(env)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "passes the host shared folder path for the folder pair to rsync" do
|
100
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
101
|
+
.with(anything, anything, env[:root_path], anything)
|
102
|
+
.and_return(rsync)
|
103
|
+
|
104
|
+
subject.call(env)
|
105
|
+
end
|
106
|
+
|
107
|
+
it "passes the folder configuration to rsync" do
|
108
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
109
|
+
.with(anything, anything, anything, config.mirror.folders[0])
|
110
|
+
.and_return(rsync)
|
111
|
+
|
112
|
+
subject.call(env)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "listens for changes in the host path" do
|
117
|
+
guard.should_receive(:listen)
|
118
|
+
|
119
|
+
subject.call(env)
|
120
|
+
end
|
121
|
+
|
122
|
+
context "with notifications in the queue" do
|
123
|
+
|
124
|
+
let (:added) { ['added'] }
|
125
|
+
let (:modified) { ['modified','modified2'] }
|
126
|
+
let (:removed) { ['removed'] }
|
127
|
+
let (:rm_exists) { true }
|
128
|
+
|
129
|
+
before (:each) do
|
130
|
+
queue.stub(:pop).and_return(
|
131
|
+
{ :added => added, :modified => modified, :removed => removed },
|
132
|
+
{ :added => added, :modified => modified, :removed => removed },
|
133
|
+
{ :quit => true })
|
134
|
+
File.stub(:exists?).with("#{env[:root_path]}/removed").and_return(rm_exists)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "runs rsync one by one for each added and modified file" do
|
138
|
+
rsync.should_receive(:run).with("added")
|
139
|
+
rsync.should_receive(:run).with("modified")
|
140
|
+
rsync.should_receive(:run).with("modified2")
|
141
|
+
|
142
|
+
subject.call(env)
|
143
|
+
sleep 0.2
|
144
|
+
end
|
145
|
+
|
146
|
+
# Sometimes Guard flags a file as deleted that still exists - during certain types of
|
147
|
+
# atomic file writes, we think. So we should delete the file remotely if so, or sync if not.
|
148
|
+
context "if deleted files have been deleted" do
|
149
|
+
let (:rm_exists) { false }
|
150
|
+
|
151
|
+
it "deletes them by SSH" do
|
152
|
+
channel.should_receive(:sudo).with('rm /var/guest/removed')
|
153
|
+
|
154
|
+
subject.call(env)
|
155
|
+
sleep 0.2
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "if deleted files have not been deleted" do
|
160
|
+
let (:rm_exists) { true }
|
161
|
+
|
162
|
+
it "runs rsync on the file" do
|
163
|
+
rsync.should_receive(:run).with('removed')
|
164
|
+
|
165
|
+
subject.call(env)
|
166
|
+
sleep 0.2
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
shared_examples "finishing mirroring" do
|
173
|
+
it "signals the Guard listener to quit"
|
174
|
+
it "waits for the Guard listener to quit"
|
175
|
+
it "processes remaining jobs in the queue"
|
176
|
+
it "calls the next middleware"
|
177
|
+
end
|
178
|
+
|
179
|
+
context "when signaled to quit" do
|
180
|
+
it_behaves_like "finishing mirroring"
|
181
|
+
end
|
182
|
+
|
183
|
+
context "when user presses q on the console" do
|
184
|
+
it_behaves_like "finishing mirroring"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
describe Vagrant::Mirror::Middleware::Sync do
|
2
|
+
# Shared mocks
|
3
|
+
let (:env) { Vagrant::Action::Environment.new }
|
4
|
+
let (:vm) { double("Vagrant::VM").as_null_object }
|
5
|
+
let (:channel) { double("Vagrant::Communication::SSH").as_null_object }
|
6
|
+
let (:ui) { double("Vagrant::UI::Interface").as_null_object }
|
7
|
+
let (:app) { double("Object").as_null_object }
|
8
|
+
let (:config) { double("Object").as_null_object }
|
9
|
+
let (:configmirror) { Vagrant::Mirror::Config.new }
|
10
|
+
let (:configvm) { Vagrant::Config::VMConfig.new }
|
11
|
+
let (:rsync) { double("Vagrant::Mirror::Rsync").as_null_object }
|
12
|
+
|
13
|
+
# Set basic stubs for shared mocks
|
14
|
+
before (:each) do
|
15
|
+
env[:vm] = vm
|
16
|
+
env[:ui] = ui
|
17
|
+
env[:root_path] = Dir.pwd
|
18
|
+
|
19
|
+
vm.stub(:config).and_return config
|
20
|
+
config.stub(:mirror).and_return configmirror
|
21
|
+
config.stub(:vm).and_return configvm
|
22
|
+
|
23
|
+
vm.stub(:channel).and_return channel
|
24
|
+
|
25
|
+
Vagrant::Mirror::Rsync.stub(:new).and_return rsync
|
26
|
+
|
27
|
+
app.stub(:call)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
subject { Vagrant::Mirror::Middleware::Sync.new(app, env) }
|
32
|
+
|
33
|
+
describe "#call" do
|
34
|
+
|
35
|
+
shared_examples "chained middleware" do
|
36
|
+
it "calls the next middleware" do
|
37
|
+
app.should_receive(:call).with(env)
|
38
|
+
|
39
|
+
subject.call(env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with no mirrored folders" do
|
44
|
+
it_behaves_like "chained middleware"
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with a mirrored folder" do
|
48
|
+
before (:each) do
|
49
|
+
config.mirror.vagrant_root '/var/guest'
|
50
|
+
config.vm.share_folder("v-root", "/vagrant", ".")
|
51
|
+
end
|
52
|
+
|
53
|
+
it_behaves_like "chained middleware"
|
54
|
+
|
55
|
+
it "creates an rsync class for the folder pair" do
|
56
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
57
|
+
.and_return(rsync)
|
58
|
+
|
59
|
+
subject.call(env)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "passes the vm to rsync" do
|
63
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
64
|
+
.with(vm, anything, anything, anything)
|
65
|
+
.and_return(rsync)
|
66
|
+
|
67
|
+
subject.call(env)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "passes the guest shared folder path for the folder pair to rsync" do
|
71
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
72
|
+
.with(anything, "/vagrant", anything, anything)
|
73
|
+
.and_return(rsync)
|
74
|
+
|
75
|
+
subject.call(env)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "passes the host shared folder path for the folder pair to rsync" do
|
79
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
80
|
+
.with(anything, anything, env[:root_path], anything)
|
81
|
+
.and_return(rsync)
|
82
|
+
|
83
|
+
subject.call(env)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "passes the folder configuration to rsync" do
|
87
|
+
Vagrant::Mirror::Rsync.should_receive(:new)
|
88
|
+
.with(anything, anything, anything, config.mirror.folders[0])
|
89
|
+
.and_return(rsync)
|
90
|
+
|
91
|
+
subject.call(env)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "runs rsync for the root of the mirrored folder" do
|
95
|
+
rsync.should_receive(:run)
|
96
|
+
.with("/")
|
97
|
+
|
98
|
+
subject.call(env)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "with two mirrored folders" do
|
103
|
+
before (:each) do
|
104
|
+
config.mirror.folder "foo", "/var/foo"
|
105
|
+
config.mirror.folder "bar", "/var/bar"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "throws an exception as this is not yet supported" do
|
109
|
+
expect { subject.call(env) }.to raise_error(Vagrant::Mirror::Errors::MultipleFoldersNotSupported)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when the mirror config includes symlinks" do
|
114
|
+
let (:host_path) { File.join(Dir.pwd, 'logs') }
|
115
|
+
|
116
|
+
before (:each) do
|
117
|
+
config.mirror.vagrant_root "/var/vagrant", { :symlinks => ['logs'] }
|
118
|
+
config.vm.share_folder("v-root", "/vagrant", ".")
|
119
|
+
|
120
|
+
File.stub(:exists?).with(host_path).and_return true
|
121
|
+
end
|
122
|
+
|
123
|
+
context "if the host folder does not exist" do
|
124
|
+
let (:host_path) { File.join(Dir.pwd, 'logs') }
|
125
|
+
|
126
|
+
before (:each) do
|
127
|
+
File.stub(:exists?).with(host_path).and_return false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "creates the folder on the host" do
|
131
|
+
FileUtils.stub(:mkdir_p).with(host_path)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "creates a symlink on the guest" do
|
136
|
+
channel.should_receive(:sudo).with('rm -f /var/vagrant/logs && mkdir -p /var/vagrant && ln -s /vagrant/logs /var/vagrant/logs')
|
137
|
+
|
138
|
+
subject.call(env)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when the mirrored config includes invalid shared folders" do
|
143
|
+
before (:each) do
|
144
|
+
config.mirror.folder "unknown", "/var/whoops"
|
145
|
+
end
|
146
|
+
|
147
|
+
it "throws an exception" do
|
148
|
+
expect { subject.call(env) }.to raise_error(Vagrant::Mirror::Errors::SharedFolderNotMapped)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
describe Vagrant::Mirror::Rsync do
|
2
|
+
|
3
|
+
# Shared mocks
|
4
|
+
let (:vm) { double("Vagrant::VM").as_null_object }
|
5
|
+
let (:channel) { double("Vagrant::Communication::SSH").as_null_object }
|
6
|
+
let (:guest_sf_path) { '/vagrant' }
|
7
|
+
let (:host_path) { 'c:/vagrant' }
|
8
|
+
let (:cfg_delete) { false }
|
9
|
+
let (:cfg_exclude) { [] }
|
10
|
+
let (:cfg_guest) { '/var/vagrant' }
|
11
|
+
let (:mirror_config) { { :name => 'v-root', :guest_path => '/var/vagrant', :delete => false,
|
12
|
+
:exclude => [], :beep => false, :symlinks => [] } }
|
13
|
+
|
14
|
+
# Set basic stubs for shared mocks
|
15
|
+
before (:each) do
|
16
|
+
vm.stub(:channel).and_return channel
|
17
|
+
|
18
|
+
File.stub(:directory?).and_return(false)
|
19
|
+
File.stub(:directory?).with('c:/vagrant/dir').and_return(true)
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { Vagrant::Mirror::Rsync.new(vm, guest_sf_path, host_path, mirror_config) }
|
23
|
+
|
24
|
+
describe "#run" do
|
25
|
+
|
26
|
+
shared_examples "running rsync to update the path" do | path, expect_source, expect_dest |
|
27
|
+
context "with no exclude paths" do
|
28
|
+
it "runs the expected rsync command" do
|
29
|
+
channel.should_receive(:sudo).with("rsync -av #{expect_source} #{expect_dest}")
|
30
|
+
|
31
|
+
subject.run(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "with exclude paths" do
|
36
|
+
let (:mirror_config) { { :name => 'v-root', :guest_path => '/var/vagrant', :delete => false,
|
37
|
+
:exclude => ['.git', 'cache'], :beep => false, :symlinks => [] } }
|
38
|
+
|
39
|
+
it "runs the expected rsync command" do
|
40
|
+
channel.should_receive(:sudo).with("rsync -av --exclude '.git' --exclude 'cache' #{expect_source} #{expect_dest}")
|
41
|
+
|
42
|
+
subject.run(path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with delete enabled" do
|
47
|
+
let (:mirror_config) { { :name => 'v-root', :guest_path => '/var/vagrant', :delete => true,
|
48
|
+
:exclude => [], :beep => false, :symlinks => [] } }
|
49
|
+
|
50
|
+
it "runs the expected rsync command" do
|
51
|
+
channel.should_receive(:sudo).with("rsync -av --del #{expect_source} #{expect_dest}")
|
52
|
+
|
53
|
+
subject.run(path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when passed an empty path" do
|
59
|
+
it_behaves_like "running rsync to update the path", '', '/vagrant/', '/var/vagrant/'
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when passed the root path" do
|
63
|
+
it_behaves_like "running rsync to update the path", '/', '/vagrant/', '/var/vagrant/'
|
64
|
+
end
|
65
|
+
|
66
|
+
context "when passed a file path" do
|
67
|
+
it_behaves_like "running rsync to update the path", 'file', '/vagrant/file', '/var/vagrant/file'
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when passed a directory path" do
|
71
|
+
it_behaves_like "running rsync to update the path", 'dir', '/vagrant/dir/', '/var/vagrant/dir/'
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|