vagrant-libvirt 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +212 -27
  3. data/lib/vagrant-libvirt/action.rb +6 -0
  4. data/lib/vagrant-libvirt/action/clean_machine_folder.rb +28 -0
  5. data/lib/vagrant-libvirt/action/create_domain.rb +26 -10
  6. data/lib/vagrant-libvirt/action/create_domain_volume.rb +57 -55
  7. data/lib/vagrant-libvirt/action/create_network_interfaces.rb +0 -3
  8. data/lib/vagrant-libvirt/action/create_networks.rb +11 -4
  9. data/lib/vagrant-libvirt/action/destroy_domain.rb +1 -1
  10. data/lib/vagrant-libvirt/action/forward_ports.rb +36 -37
  11. data/lib/vagrant-libvirt/action/halt_domain.rb +25 -9
  12. data/lib/vagrant-libvirt/action/handle_box_image.rb +162 -74
  13. data/lib/vagrant-libvirt/action/is_running.rb +1 -3
  14. data/lib/vagrant-libvirt/action/is_suspended.rb +4 -4
  15. data/lib/vagrant-libvirt/action/wait_till_up.rb +1 -25
  16. data/lib/vagrant-libvirt/cap/{mount_p9.rb → mount_9p.rb} +2 -2
  17. data/lib/vagrant-libvirt/cap/mount_virtiofs.rb +37 -0
  18. data/lib/vagrant-libvirt/cap/{synced_folder.rb → synced_folder_9p.rb} +4 -5
  19. data/lib/vagrant-libvirt/cap/synced_folder_virtiofs.rb +109 -0
  20. data/lib/vagrant-libvirt/config.rb +24 -2
  21. data/lib/vagrant-libvirt/errors.rb +24 -1
  22. data/lib/vagrant-libvirt/plugin.rb +13 -5
  23. data/lib/vagrant-libvirt/templates/domain.xml.erb +7 -6
  24. data/lib/vagrant-libvirt/templates/private_network.xml.erb +1 -1
  25. data/lib/vagrant-libvirt/util/network_util.rb +21 -3
  26. data/lib/vagrant-libvirt/version +1 -1
  27. data/locales/en.yml +12 -0
  28. data/spec/spec_helper.rb +9 -1
  29. data/spec/support/matchers/have_file_content.rb +63 -0
  30. data/spec/unit/action/clean_machine_folder_spec.rb +48 -0
  31. data/spec/unit/action/create_domain_spec.rb +6 -0
  32. data/spec/unit/action/create_domain_volume_spec.rb +102 -0
  33. data/spec/unit/action/create_domain_volume_spec/one_disk_in_storage.xml +21 -0
  34. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_0.xml +21 -0
  35. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_1.xml +21 -0
  36. data/spec/unit/action/create_domain_volume_spec/three_disks_in_storage_disk_2.xml +21 -0
  37. data/spec/unit/action/destroy_domain_spec.rb +1 -1
  38. data/spec/unit/action/forward_ports_spec.rb +202 -0
  39. data/spec/unit/action/halt_domain_spec.rb +90 -0
  40. data/spec/unit/action/handle_box_image_spec.rb +363 -0
  41. data/spec/unit/action/wait_till_up_spec.rb +1 -23
  42. data/spec/unit/templates/domain_all_settings.xml +8 -0
  43. data/spec/unit/templates/domain_spec.rb +20 -1
  44. metadata +41 -4
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'support/sharedcontext'
3
+ require 'support/libvirt_context'
4
+
5
+ require 'vagrant-libvirt/errors'
6
+ require 'vagrant-libvirt/action/forward_ports'
7
+
8
+ describe VagrantPlugins::ProviderLibvirt::Action::ForwardPorts do
9
+ subject { described_class.new(app, env) }
10
+
11
+ include_context 'unit'
12
+
13
+ let(:machine_config) { double("machine_config") }
14
+ let(:vm_config) { double("vm_config") }
15
+ let(:provider_config) { double("provider_config") }
16
+
17
+ before (:each) do
18
+ allow(machine).to receive(:config).and_return(machine_config)
19
+ allow(machine).to receive(:provider_config).and_return(provider_config)
20
+ allow(machine_config).to receive(:vm).and_return(vm_config)
21
+ allow(vm_config).to receive(:networks).and_return([])
22
+ allow(provider_config).to receive(:forward_ssh_port).and_return(false)
23
+ end
24
+
25
+ describe '#call' do
26
+ context 'with none defined' do
27
+ it 'should skip calling forward_ports' do
28
+ expect(subject).to_not receive(:forward_ports)
29
+ expect(subject.call(env)).to be_nil
30
+ end
31
+ end
32
+
33
+ context 'with network including one forwarded port' do
34
+ let(:networks) { [
35
+ [:private_network, {:ip=>"10.20.30.40", :protocol=>"tcp", :id=>"6b8175ed-3220-4b63-abaf-0bb8d7cdd723"}],
36
+ [:forwarded_port, port_options],
37
+ ]}
38
+
39
+ let(:port_options){ {guest: 80, host: 8080} }
40
+
41
+ it 'should compile a single port forward to set up' do
42
+ expect(vm_config).to receive(:networks).and_return(networks)
43
+ expect(ui).to_not receive(:warn)
44
+ expect(subject).to receive(:forward_ports).and_return(nil)
45
+
46
+ expect(subject.call(env)).to be_nil
47
+
48
+ expect(env[:forwarded_ports]).to eq([networks[1][1]])
49
+ end
50
+
51
+ context 'when host port in protected range' do
52
+ let(:port_options){ {guest: 8080, host: 80} }
53
+
54
+ it 'should emit a warning' do
55
+ expect(vm_config).to receive(:networks).and_return(networks)
56
+ expect(ui).to receive(:warn).with(include("You are trying to forward to privileged ports"))
57
+ expect(subject).to receive(:forward_ports).and_return(nil)
58
+
59
+ expect(subject.call(env)).to be_nil
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'when udp protocol is selected' do
65
+ let(:port_options){ {guest: 80, host: 8080, protocol: "udp"} }
66
+
67
+ it 'should skip and emit warning' do
68
+ expect(vm_config).to receive(:networks).and_return([[:forwarded_port, port_options]])
69
+ expect(ui).to receive(:warn).with("Forwarding UDP ports is not supported. Ignoring.")
70
+ expect(subject).to_not receive(:forward_ports)
71
+
72
+ expect(subject.call(env)).to be_nil
73
+ end
74
+ end
75
+
76
+ context 'when default ssh port forward provided' do
77
+ let(:networks){ [
78
+ [:private_network, {:ip=>"10.20.30.40", :protocol=>"tcp", :id=>"6b8175ed-3220-4b63-abaf-0bb8d7cdd723"}],
79
+ [:forwarded_port, {guest: 80, host: 8080}],
80
+ [:forwarded_port, {guest: 22, host: 2222, host_ip: '127.0.0.1', id: 'ssh'}],
81
+ ]}
82
+
83
+ context 'with default config' do
84
+ it 'should not forward the ssh port' do
85
+ expect(vm_config).to receive(:networks).and_return(networks)
86
+ expect(subject).to receive(:forward_ports)
87
+
88
+ expect(subject.call(env)).to be_nil
89
+
90
+ expect(env[:forwarded_ports]).to eq([networks[1][1]])
91
+ end
92
+ end
93
+
94
+ context 'with forward_ssh_port enabled' do
95
+ before do
96
+ allow(provider_config).to receive(:forward_ssh_port).and_return(true)
97
+ end
98
+
99
+ it 'should forward the port' do
100
+ expect(vm_config).to receive(:networks).and_return(networks)
101
+ expect(subject).to receive(:forward_ports)
102
+
103
+ expect(subject.call(env)).to be_nil
104
+
105
+ expect(env[:forwarded_ports]).to eq(networks.drop(1).map { |_, opts| opts })
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#forward_ports' do
112
+ let(:pid_dir){ machine.data_dir.join('pids') }
113
+
114
+ before (:each) do
115
+ allow(env).to receive(:[]).and_call_original
116
+ allow(machine).to receive(:ssh_info).and_return(
117
+ {
118
+ :host => "localhost",
119
+ :username => "vagrant",
120
+ :port => 22,
121
+ :private_key_path => ["/home/test/.ssh/id_rsa"],
122
+ }
123
+ )
124
+ allow(provider_config).to receive(:proxy_command).and_return(nil)
125
+ end
126
+
127
+ context 'with port to forward' do
128
+ let(:port_options){ {guest: 80, host: 8080, guest_ip: "192.168.1.121"} }
129
+
130
+ it 'should spawn ssh to setup forwarding' do
131
+ expect(env).to receive(:[]).with(:forwarded_ports).and_return([port_options])
132
+ expect(ui).to receive(:info).with("#{port_options[:guest]} (guest) => #{port_options[:host]} (host) (adapter eth0)")
133
+ expect(subject).to receive(:spawn).with(/ssh -n -o User=vagrant -o Port=22.*-L \*:8080:192.168.1.121:80 -N localhost/, anything).and_return(9999)
134
+
135
+ expect(subject.forward_ports(env)).to eq([port_options])
136
+
137
+ expect(pid_dir.join('ssh_8080.pid')).to have_file_content("9999")
138
+ end
139
+ end
140
+
141
+ context 'with privileged host port' do
142
+ let(:port_options){ {guest: 80, host: 80, guest_ip: "192.168.1.121"} }
143
+
144
+ it 'should spawn ssh to setup forwarding' do
145
+ expect(env).to receive(:[]).with(:forwarded_ports).and_return([port_options])
146
+ expect(ui).to receive(:info).with("#{port_options[:guest]} (guest) => #{port_options[:host]} (host) (adapter eth0)")
147
+ expect(ui).to receive(:info).with('Requesting sudo for host port(s) <= 1024')
148
+ expect(subject).to receive(:system).with('sudo -v').and_return(true)
149
+ expect(subject).to receive(:spawn).with(/sudo ssh -n -o User=vagrant -o Port=22.*-L \*:80:192.168.1.121:80 -N localhost/, anything).and_return(10000)
150
+
151
+ expect(subject.forward_ports(env)).to eq([port_options])
152
+
153
+ expect(pid_dir.join('ssh_80.pid')).to have_file_content("10000")
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+ describe VagrantPlugins::ProviderLibvirt::Action::ClearForwardedPorts do
160
+ subject { described_class.new(app, env) }
161
+
162
+ include_context 'unit'
163
+ include_context 'libvirt'
164
+
165
+ describe '#call' do
166
+ context 'no forwarded ports' do
167
+ it 'should skip checking if pids are running' do
168
+ expect(subject).to_not receive(:ssh_pid?)
169
+ expect(logger).to receive(:info).with('No ssh pids found')
170
+
171
+ expect(subject.call(env)).to be_nil
172
+ end
173
+ end
174
+
175
+ context 'multiple forwarded ports' do
176
+ before do
177
+ data_dir = machine.data_dir.join('pids')
178
+ data_dir.mkdir unless data_dir.directory?
179
+
180
+ [
181
+ {:port => '8080', :pid => '10001'},
182
+ {:port => '8081', :pid => '10002'},
183
+ ].each do |port_pid|
184
+ File.write(data_dir.to_s + "/ssh_#{port_pid[:port]}.pid", port_pid[:pid])
185
+ end
186
+ end
187
+ it 'should terminate each of the processes' do
188
+ expect(logger).to receive(:info).with(no_args) # don't know how to test translations from vagrant
189
+ expect(subject).to receive(:ssh_pid?).with("10001").and_return(true)
190
+ expect(subject).to receive(:ssh_pid?).with("10002").and_return(true)
191
+ expect(logger).to receive(:debug).with(/Killing pid/).twice()
192
+ expect(logger).to receive(:info).with('Removing ssh pid files')
193
+ expect(subject).to receive(:system).with("kill 10001")
194
+ expect(subject).to receive(:system).with("kill 10002")
195
+
196
+ expect(subject.call(env)).to be_nil
197
+
198
+ expect(Dir.entries(machine.data_dir.join('pids'))).to match_array(['.', '..'])
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+ require 'support/sharedcontext'
3
+ require 'support/libvirt_context'
4
+ require 'vagrant-libvirt/action/destroy_domain'
5
+
6
+ describe VagrantPlugins::ProviderLibvirt::Action::HaltDomain do
7
+ subject { described_class.new(app, env) }
8
+
9
+ include_context 'unit'
10
+ include_context 'libvirt'
11
+
12
+ let(:libvirt_domain) { double('libvirt_domain') }
13
+ let(:servers) { double('servers') }
14
+
15
+ describe '#call' do
16
+ before do
17
+ allow_any_instance_of(VagrantPlugins::ProviderLibvirt::Driver)
18
+ .to receive(:connection).and_return(connection)
19
+ allow(connection).to receive(:servers).and_return(servers)
20
+ allow(servers).to receive(:get).and_return(domain)
21
+ # always see this at the start of #call
22
+ expect(ui).to receive(:info).with('Halting domain...')
23
+ end
24
+
25
+ context 'with graceful timeout' do
26
+ it "should shutdown" do
27
+ expect(guest).to receive(:capability).with(:halt).and_return(true)
28
+ expect(domain).to receive(:wait_for).with(60).and_return(false)
29
+ expect(subject.call(env)).to be_nil
30
+ end
31
+
32
+ context 'when halt fails' do
33
+ before do
34
+ expect(logger).to receive(:info).with('Trying Libvirt graceful shutdown.')
35
+ expect(guest).to receive(:capability).with(:halt).and_raise(IOError)
36
+ expect(domain).to receive(:state).and_return('running')
37
+ end
38
+
39
+ it "should call shutdown" do
40
+ expect(domain).to receive(:shutdown)
41
+ expect(domain).to receive(:wait_for).with(60).and_return(false)
42
+ expect(subject.call(env)).to be_nil
43
+ end
44
+
45
+ context 'when shutdown fails' do
46
+ it "should call power off" do
47
+ expect(logger).to receive(:error).with('Failed to shutdown cleanly. Calling force poweroff.')
48
+ expect(domain).to receive(:shutdown).and_raise(IOError)
49
+ expect(domain).to receive(:poweroff)
50
+ expect(subject.call(env)).to be_nil
51
+ end
52
+ end
53
+
54
+ context 'when shutdown exceeds the timeout' do
55
+ it "should call poweroff" do
56
+ expect(logger).to receive(:info).with('VM is still running. Calling force poweroff.')
57
+ expect(domain).to receive(:shutdown).and_raise(Timeout::Error)
58
+ expect(domain).to receive(:poweroff)
59
+ expect(subject.call(env)).to be_nil
60
+ end
61
+ end
62
+ end
63
+
64
+ context 'when halt exceeds the timeout' do
65
+ before do
66
+ expect(logger).to_not receive(:info).with('Trying Libvirt graceful shutdown.')
67
+ expect(guest).to receive(:capability).with(:halt).and_raise(Timeout::Error)
68
+ end
69
+
70
+ it "should call poweroff" do
71
+ expect(logger).to receive(:info).with('VM is still running. Calling force poweroff.')
72
+ expect(domain).to receive(:poweroff)
73
+ expect(subject.call(env)).to be_nil
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'with force halt enabled' do
79
+ before do
80
+ allow(env).to receive(:[]).and_call_original
81
+ expect(env).to receive(:[]).with(:force_halt).and_return(true)
82
+ end
83
+
84
+ it "should just call poweroff" do
85
+ expect(domain).to receive(:poweroff)
86
+ expect(subject.call(env)).to be_nil
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,363 @@
1
+ require 'spec_helper'
2
+ require 'support/sharedcontext'
3
+ require 'support/libvirt_context'
4
+
5
+ require 'vagrant-libvirt/action/destroy_domain'
6
+
7
+ describe VagrantPlugins::ProviderLibvirt::Action::HandleBoxImage do
8
+ subject { described_class.new(app, env) }
9
+
10
+ include_context 'unit'
11
+ include_context 'libvirt'
12
+
13
+ let(:libvirt_client) { double('libvirt_client') }
14
+ let(:volumes) { double('volumes') }
15
+ let(:all) { double('all') }
16
+ let(:box_volume) { double('box_volume') }
17
+ let(:fog_volume) { double('fog_volume') }
18
+
19
+ describe '#call' do
20
+ before do
21
+ allow_any_instance_of(VagrantPlugins::ProviderLibvirt::Driver)
22
+ .to receive(:connection).and_return(connection)
23
+ allow(connection).to receive(:client).and_return(libvirt_client)
24
+ allow(connection).to receive(:volumes).and_return(volumes)
25
+ allow(volumes).to receive(:all).and_return(all)
26
+ allow(env[:ui]).to receive(:clear_line)
27
+
28
+ end
29
+
30
+ context 'when one disk in metadata.json' do
31
+ before do
32
+ allow(all).to receive(:first).and_return(box_volume)
33
+ allow(box_volume).to receive(:id).and_return(1)
34
+ allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
35
+ allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
36
+ allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
37
+ 'virtual_size'=> 5,
38
+ 'format' => 'qcow2'
39
+ ]
40
+ }
41
+ allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
42
+ '/test/'.concat(arg.to_s)
43
+ end
44
+ end
45
+
46
+ it 'should have one disk in machine env' do
47
+ expect(subject.call(env)).to be_nil
48
+ expect(env[:box_volume_number]).to eq(1)
49
+ expect(env[:box_volumes]).to eq(
50
+ [
51
+ {
52
+ :path=>"/test/box.img",
53
+ :name=>"test_vagrant_box_image_1.1.1_box.img",
54
+ :virtual_size=>5,
55
+ :format=>"qcow2"
56
+ }
57
+ ]
58
+ )
59
+ end
60
+
61
+ context 'when disk image not in storage pool' do
62
+ before do
63
+ allow(File).to receive(:exist?).and_return(true)
64
+ allow(File).to receive(:size).and_return(5*1024*1024*1024)
65
+ allow(all).to receive(:first).and_return(nil)
66
+ allow(subject).to receive(:upload_image).and_return(true)
67
+ allow(volumes).to receive(:create).and_return(fog_volume)
68
+ end
69
+
70
+ it 'should upload disk' do
71
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
72
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_box.img in storage pool default.')
73
+ expect(volumes).to receive(:create).with(
74
+ hash_including(
75
+ :name => "test_vagrant_box_image_1.1.1_box.img",
76
+ :allocation => "5120M",
77
+ :capacity => "5G",
78
+ )
79
+ )
80
+ expect(subject).to receive(:upload_image)
81
+ expect(subject.call(env)).to be_nil
82
+ end
83
+ end
84
+
85
+ context 'when disk image already in storage pool' do
86
+ before do
87
+ allow(all).to receive(:first).and_return(box_volume)
88
+ allow(box_volume).to receive(:id).and_return(1)
89
+ end
90
+
91
+ it 'should skip disk upload' do
92
+ expect(volumes).not_to receive(:create)
93
+ expect(subject).not_to receive(:upload_image)
94
+ expect(subject.call(env)).to be_nil
95
+ end
96
+ end
97
+ end
98
+
99
+ context 'when three disks in metadata.json' do
100
+ let(:status) { double }
101
+
102
+ before do
103
+ allow(all).to receive(:first).and_return(box_volume)
104
+ allow(box_volume).to receive(:id).and_return(1)
105
+ allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
106
+ allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
107
+ allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
108
+ 'disks' => [
109
+ {
110
+ 'path' => 'box.img',
111
+ 'name' => 'send_box_name',
112
+ },
113
+ {
114
+ 'path' => 'disk.qcow2',
115
+ },
116
+ {
117
+ 'path' => 'box_2.img',
118
+ },
119
+ ],
120
+ ]}
121
+ allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
122
+ '/test/'.concat(arg.to_s)
123
+ end
124
+ allow(status).to receive(:success?).and_return(true)
125
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/box.img').and_return([
126
+ "image: /test/box.img\nfile format: qcow2\nvirtual size: 5 GiB (5368709120 bytes)\ndisk size: 1.45 GiB\n", "", status
127
+ ])
128
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/disk.qcow2').and_return([
129
+ "image: /test/disk.qcow2\nfile format: qcow2\nvirtual size: 10 GiB (10737418240 bytes)\ndisk size: 1.45 GiB\n", "", status
130
+ ])
131
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/box_2.img').and_return([
132
+ "image: /test/box_2.img\nfile format: qcow2\nvirtual size: 20 GiB (21474836480 bytes)\ndisk size: 1.45 GiB\n", "", status
133
+ ])
134
+ end
135
+
136
+ it 'should have three disks in machine env' do
137
+ expect(subject.call(env)).to be_nil
138
+ expect(env[:box_volume_number]).to eq(3)
139
+ expect(env[:box_volumes]).to eq(
140
+ [
141
+ {
142
+ :path=>"/test/box.img",
143
+ :name=>"test_vagrant_box_image_1.1.1_send_box_name.img",
144
+ :virtual_size=>5,
145
+ :format=>"qcow2"
146
+ },
147
+ {
148
+ :path=>"/test/disk.qcow2",
149
+ :name=>"test_vagrant_box_image_1.1.1_disk.img",
150
+ :virtual_size=>10,
151
+ :format=>"qcow2"
152
+ },
153
+ {
154
+ :path=>"/test/box_2.img",
155
+ :name=>"test_vagrant_box_image_1.1.1_box_2.img",
156
+ :virtual_size=>20,
157
+ :format=>"qcow2"
158
+ }
159
+ ]
160
+ )
161
+ end
162
+
163
+ context 'when none of the disks in storage pool' do
164
+ before do
165
+ allow(File).to receive(:exist?).and_return(true)
166
+ allow(File).to receive(:size).and_return(5*1024*1024*1024, 10*1024*1024*1024, 20*1024*1024*1024)
167
+ allow(all).to receive(:first).and_return(nil)
168
+ allow(subject).to receive(:upload_image).and_return(true)
169
+ allow(volumes).to receive(:create).and_return(fog_volume)
170
+ end
171
+
172
+ it 'should upload all 3 disks' do
173
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
174
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_send_box_name.img in storage pool default.')
175
+ expect(volumes).to receive(:create).with(
176
+ hash_including(
177
+ :name => "test_vagrant_box_image_1.1.1_send_box_name.img",
178
+ :allocation => "5120M",
179
+ :capacity => "5G",
180
+ )
181
+ )
182
+ expect(subject).to receive(:upload_image)
183
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
184
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_disk.img in storage pool default.')
185
+ expect(volumes).to receive(:create).with(
186
+ hash_including(
187
+ :name => "test_vagrant_box_image_1.1.1_disk.img",
188
+ :allocation => "10240M",
189
+ :capacity => "10G",
190
+ )
191
+ )
192
+ expect(subject).to receive(:upload_image)
193
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
194
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_box_2.img in storage pool default.')
195
+ expect(volumes).to receive(:create).with(
196
+ hash_including(
197
+ :name => "test_vagrant_box_image_1.1.1_box_2.img",
198
+ :allocation => "20480M",
199
+ :capacity => "20G",
200
+ )
201
+ )
202
+ expect(subject).to receive(:upload_image)
203
+
204
+ expect(subject.call(env)).to be_nil
205
+ end
206
+ end
207
+
208
+ context 'when only disk 0 in storage pool' do
209
+ before do
210
+ allow(File).to receive(:exist?).and_return(true)
211
+ allow(File).to receive(:size).and_return(10*1024*1024*1024, 20*1024*1024*1024)
212
+ allow(all).to receive(:first).and_return(box_volume, nil, nil)
213
+ allow(box_volume).to receive(:id).and_return(1)
214
+ allow(subject).to receive(:upload_image).and_return(true)
215
+ allow(volumes).to receive(:create).and_return(fog_volume)
216
+ end
217
+
218
+ it 'upload disks 1 and 2 only' do
219
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
220
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_disk.img in storage pool default.')
221
+ expect(volumes).to receive(:create).with(hash_including(:name => "test_vagrant_box_image_1.1.1_disk.img"))
222
+ expect(subject).to receive(:upload_image)
223
+ expect(ui).to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
224
+ expect(logger).to receive(:info).with('Creating volume test_vagrant_box_image_1.1.1_box_2.img in storage pool default.')
225
+ expect(volumes).to receive(:create).with(hash_including(:name => "test_vagrant_box_image_1.1.1_box_2.img"))
226
+ expect(subject).to receive(:upload_image)
227
+
228
+ expect(subject.call(env)).to be_nil
229
+ end
230
+ end
231
+
232
+ context 'when has all disks on storage pool' do
233
+ before do
234
+ allow(all).to receive(:first).and_return(box_volume)
235
+ allow(box_volume).to receive(:id).and_return(1)
236
+ end
237
+
238
+ it 'should skip disk upload' do
239
+ expect(ui).not_to receive(:info).with('Uploading base box image as volume into Libvirt storage...')
240
+ expect(volumes).not_to receive(:create)
241
+ expect(subject).not_to receive(:upload_image)
242
+ expect(subject.call(env)).to be_nil
243
+ end
244
+ end
245
+ end
246
+
247
+ context 'when wrong box format in metadata.json' do
248
+ before do
249
+ allow(all).to receive(:first).and_return(box_volume)
250
+ allow(box_volume).to receive(:id).and_return(1)
251
+ allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
252
+ allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
253
+ allow(env[:machine]).to receive_message_chain("box.metadata") { Hash[
254
+ 'virtual_size'=> 5,
255
+ 'format' => 'wrongFormat'
256
+ ]
257
+ }
258
+ allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
259
+ '/test/'.concat(arg.to_s)
260
+ end
261
+ end
262
+
263
+ it 'should raise WrongBoxFormatSet exception' do
264
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::WrongBoxFormatSet)
265
+ end
266
+
267
+ end
268
+
269
+ context 'when invalid format in metadata.json' do
270
+ let(:status) { double }
271
+
272
+ before do
273
+ allow(all).to receive(:first).and_return(box_volume)
274
+ allow(box_volume).to receive(:id).and_return(1)
275
+ allow(env[:machine]).to receive_message_chain("box.name") { 'test' }
276
+ allow(env[:machine]).to receive_message_chain("box.version") { '1.1.1' }
277
+ allow(env[:machine]).to receive_message_chain("box.metadata") { box_metadata }
278
+ allow(env[:machine]).to receive_message_chain("box.directory.join") do |arg|
279
+ '/test/'.concat(arg.to_s)
280
+ end
281
+ allow(status).to receive(:success?).and_return(true)
282
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/box.img').and_return([
283
+ "image: /test/box.img\nfile format: qcow2\nvirtual size: 5 GiB (5368709120 bytes)\ndisk size: 1.45 GiB\n", "", status
284
+ ])
285
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/disk.qcow2').and_return([
286
+ "image: /test/disk.qcow2\nfile format: qcow2\nvirtual size: 10 GiB (10737418240 bytes)\ndisk size: 1.45 GiB\n", "", status
287
+ ])
288
+ allow(Open3).to receive(:capture3).with('qemu-img', 'info', '/test/box_2.img').and_return([
289
+ "image: /test/box_2.img\nfile format: qcow2\nvirtual size: 20 GiB (21474836480 bytes)\ndisk size: 1.45 GiB\n", "", status
290
+ ])
291
+ end
292
+
293
+ context 'with one disk having wrong disk format' do
294
+ let(:box_metadata) {
295
+ Hash[
296
+ 'disks' => [
297
+ {
298
+ 'path' => 'box.img',
299
+ 'name' =>'send_box_name.img',
300
+ 'format' => 'wrongFormat'
301
+ },
302
+ {
303
+ 'path' => 'disk.qcow2',
304
+ },
305
+ {
306
+ 'path' => 'box_2.img',
307
+ },
308
+ ],
309
+ ]
310
+ }
311
+
312
+ it 'should be ignored' do
313
+ expect(subject.call(env)).to be_nil
314
+ end
315
+ end
316
+
317
+ context 'with one disk missing path' do
318
+ let(:box_metadata) {
319
+ Hash[
320
+ 'disks' => [
321
+ {
322
+ 'path' => 'box.img',
323
+ },
324
+ {
325
+ 'name' => 'send_box_name',
326
+ },
327
+ {
328
+ 'path' => 'box_2.img',
329
+ },
330
+ ],
331
+ ]
332
+ }
333
+
334
+ it 'should raise an exception' do
335
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::BoxFormatMissingAttribute, /: 'disks\[1\]\['path'\]'/)
336
+ end
337
+ end
338
+
339
+ context 'with one disk name duplicating a path of another' do
340
+ let(:box_metadata) {
341
+ Hash[
342
+ 'disks' => [
343
+ {
344
+ 'path' => 'box.img',
345
+ 'name' => 'box_2',
346
+ },
347
+ {
348
+ 'path' => 'disk.qcow2',
349
+ },
350
+ {
351
+ 'path' => 'box_2.img',
352
+ },
353
+ ],
354
+ ]
355
+ }
356
+
357
+ it 'should raise an exception' do
358
+ expect{ subject.call(env) }.to raise_error(VagrantPlugins::ProviderLibvirt::Errors::BoxFormatDuplicateVolume, /test_vagrant_box_image_1.1.1_box_2.img.*'disks\[2\]'.*'disks\[0\]'/)
359
+ end
360
+ end
361
+ end
362
+ end
363
+ end