vagrant-mirror 0.1.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|