vagrant-libvirt 0.4.1 → 0.5.0

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.
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