vagrant-openstack-provider 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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -2
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile +8 -15
  5. data/Vagrantfile +12 -10
  6. data/gemfiles/latest_stable.gemfile +6 -16
  7. data/gemfiles/minimal_release.gemfile +10 -0
  8. data/gemfiles/previous_release.gemfile +6 -16
  9. data/lib/vagrant-openstack-provider/action/connect_openstack.rb +2 -1
  10. data/lib/vagrant-openstack-provider/action/create_server.rb +40 -248
  11. data/lib/vagrant-openstack-provider/action/read_ssh_info.rb +30 -29
  12. data/lib/vagrant-openstack-provider/action/sync_folders.rb +19 -9
  13. data/lib/vagrant-openstack-provider/action/wait_accessible.rb +58 -0
  14. data/lib/vagrant-openstack-provider/action/wait_active.rb +5 -3
  15. data/lib/vagrant-openstack-provider/action/wait_stop.rb +5 -3
  16. data/lib/vagrant-openstack-provider/action.rb +19 -11
  17. data/lib/vagrant-openstack-provider/client/http_utils.rb +3 -2
  18. data/lib/vagrant-openstack-provider/client/neutron.rb +11 -1
  19. data/lib/vagrant-openstack-provider/client/nova.rb +49 -30
  20. data/lib/vagrant-openstack-provider/command/abstract_command.rb +10 -1
  21. data/lib/vagrant-openstack-provider/command/flavor_list.rb +1 -1
  22. data/lib/vagrant-openstack-provider/command/floatingip_list.rb +1 -1
  23. data/lib/vagrant-openstack-provider/command/image_list.rb +1 -1
  24. data/lib/vagrant-openstack-provider/command/main.rb +2 -1
  25. data/lib/vagrant-openstack-provider/command/network_list.rb +8 -2
  26. data/lib/vagrant-openstack-provider/command/reset.rb +21 -0
  27. data/lib/vagrant-openstack-provider/command/utils.rb +1 -1
  28. data/lib/vagrant-openstack-provider/command/volume_list.rb +1 -1
  29. data/lib/vagrant-openstack-provider/config.rb +16 -1
  30. data/lib/vagrant-openstack-provider/config_resolver.rb +262 -0
  31. data/lib/vagrant-openstack-provider/errors.rb +40 -0
  32. data/lib/vagrant-openstack-provider/plugin.rb +3 -3
  33. data/lib/vagrant-openstack-provider/utils.rb +21 -0
  34. data/lib/vagrant-openstack-provider/version.rb +1 -1
  35. data/locales/en.yml +35 -1
  36. data/spec/vagrant-openstack-provider/action/create_server_spec.rb +121 -368
  37. data/spec/vagrant-openstack-provider/action/delete_server_spec.rb +54 -0
  38. data/spec/vagrant-openstack-provider/action/message_spec.rb +34 -0
  39. data/spec/vagrant-openstack-provider/action/read_ssh_info_spec.rb +34 -17
  40. data/spec/vagrant-openstack-provider/action/read_state_spec.rb +70 -0
  41. data/spec/vagrant-openstack-provider/action/resume_server_spec.rb +50 -0
  42. data/spec/vagrant-openstack-provider/action/start_server_spec.rb +50 -0
  43. data/spec/vagrant-openstack-provider/action/stop_server_spec.rb +50 -0
  44. data/spec/vagrant-openstack-provider/action/suspend_server_spec.rb +50 -0
  45. data/spec/vagrant-openstack-provider/action/sync_folders_spec.rb +155 -0
  46. data/spec/vagrant-openstack-provider/action/wait_accessible_spec.rb +68 -0
  47. data/spec/vagrant-openstack-provider/action/wait_active_spec.rb +47 -0
  48. data/spec/vagrant-openstack-provider/action/wait_stop_spec.rb +47 -0
  49. data/spec/vagrant-openstack-provider/action_spec.rb +121 -0
  50. data/spec/vagrant-openstack-provider/client/cinder_spec.rb +1 -1
  51. data/spec/vagrant-openstack-provider/client/keystone_spec.rb +1 -1
  52. data/spec/vagrant-openstack-provider/client/neutron_spec.rb +37 -1
  53. data/spec/vagrant-openstack-provider/client/nova_spec.rb +60 -7
  54. data/spec/vagrant-openstack-provider/client/utils_spec.rb +1 -1
  55. data/spec/vagrant-openstack-provider/command/flavor_list_spec.rb +44 -0
  56. data/spec/vagrant-openstack-provider/command/floatingip_list_spec.rb +19 -2
  57. data/spec/vagrant-openstack-provider/command/image_list_spec.rb +48 -0
  58. data/spec/vagrant-openstack-provider/command/network_list_spec.rb +67 -0
  59. data/spec/vagrant-openstack-provider/command/reset_spec.rb +25 -0
  60. data/spec/vagrant-openstack-provider/command/volume_list_spec.rb +10 -2
  61. data/spec/vagrant-openstack-provider/config_resolver_spec.rb +680 -0
  62. data/spec/vagrant-openstack-provider/config_spec.rb +15 -1
  63. data/spec/vagrant-openstack-provider/spec_helper.rb +3 -0
  64. data/spec/vagrant-openstack-provider/utils_spec.rb +103 -0
  65. data/vagrant-openstack-provider.gemspec +4 -2
  66. metadata +78 -11
  67. data/Appraisals +0 -13
  68. data/gemfiles/oldest_current.gemfile +0 -20
@@ -47,7 +47,7 @@ describe VagrantPlugins::Openstack::KeystoneClient do
47
47
  end
48
48
 
49
49
  before :each do
50
- @keystone_client = VagrantPlugins::Openstack::KeystoneClient.instance
50
+ @keystone_client = VagrantPlugins::Openstack.keystone
51
51
  end
52
52
 
53
53
  context 'with good credentials' do
@@ -14,7 +14,7 @@ describe VagrantPlugins::Openstack::NeutronClient do
14
14
  session.token = '123456'
15
15
  session.project_id = 'a1b2c3'
16
16
  session.endpoints = { network: 'http://neutron' }
17
- @neutron_client = VagrantPlugins::Openstack::NeutronClient.instance
17
+ @neutron_client = VagrantPlugins::Openstack.neutron
18
18
  end
19
19
 
20
20
  describe 'get_private_networks' do
@@ -51,6 +51,42 @@ describe VagrantPlugins::Openstack::NeutronClient do
51
51
  end
52
52
  end
53
53
 
54
+ describe 'get_all_networks' do
55
+ context 'with token' do
56
+ it 'returns all networks for project in session' do
57
+
58
+ stub_request(:get, 'http://neutron/networks')
59
+ .with(
60
+ headers:
61
+ {
62
+ 'Accept' => 'application/json',
63
+ 'X-Auth-Token' => '123456'
64
+ })
65
+ .to_return(
66
+ status: 200,
67
+ body: '
68
+ {
69
+ "networks": [
70
+ { "name": "PublicNetwork", "tenant_id": "admin-tenant-id", "id": "net-pub" },
71
+ { "name": "net1", "tenant_id": "a1b2c3", "id": "net-1" },
72
+ { "name": "net2", "tenant_id": "a1b2c3", "id": "net-2" }
73
+ ]
74
+ }
75
+ ')
76
+
77
+ networks = @neutron_client.get_all_networks(env)
78
+
79
+ expect(networks.length).to eq(3)
80
+ expect(networks[0].id).to eq('net-pub')
81
+ expect(networks[0].name).to eq('PublicNetwork')
82
+ expect(networks[1].id).to eq('net-1')
83
+ expect(networks[1].name).to eq('net1')
84
+ expect(networks[2].id).to eq('net-2')
85
+ expect(networks[2].name).to eq('net2')
86
+ end
87
+ end
88
+ end
89
+
54
90
  describe 'get_api_version_list' do
55
91
  context 'basic' do
56
92
  it 'returns version list' do
@@ -39,7 +39,29 @@ describe VagrantPlugins::Openstack::NovaClient do
39
39
  session.token = '123456'
40
40
  session.project_id = 'a1b2c3'
41
41
  session.endpoints = { compute: 'http://nova/a1b2c3' }
42
- @nova_client = VagrantPlugins::Openstack::NovaClient.instance
42
+ @nova_client = VagrantPlugins::Openstack.nova
43
+ end
44
+
45
+ describe 'instance_exists' do
46
+ context 'instance not found' do
47
+ it 'raise an InstanceNotFound error' do
48
+ stub_request(:post, 'http://nova/a1b2c3/servers/o1o2o3/action')
49
+ .with(
50
+ body: '{"os-start":null}',
51
+ headers:
52
+ {
53
+ 'Accept' => 'application/json',
54
+ 'Content-Type' => 'application/json',
55
+ 'X-Auth-Token' => '123456'
56
+ })
57
+ .to_return(
58
+ status: 404,
59
+ body: '{"itemNotFound": {"message": "Instance could not be found", "code": 404}}')
60
+
61
+ expect { @nova_client.start_server(env, 'o1o2o3') }.to raise_error(VagrantPlugins::Openstack::Errors::InstanceNotFound)
62
+
63
+ end
64
+ end
43
65
  end
44
66
 
45
67
  describe 'get_all_flavors' do
@@ -119,7 +141,7 @@ describe VagrantPlugins::Openstack::NovaClient do
119
141
  stub_request(:post, 'http://nova/a1b2c3/servers')
120
142
  .with(
121
143
  body: '{"server":{"name":"inst","imageRef":"img","flavorRef":"flav","key_name":"key",'\
122
- '"security_groups":["default"],"user_data":"user_data_test","metadata":"metadata_test"},'\
144
+ '"security_groups":[{"name":"default"}],"user_data":"dXNlcl9kYXRhX3Rlc3Q=\n","metadata":"metadata_test"},'\
123
145
  '"scheduler_hints":"sched_hints_test"}',
124
146
  headers:
125
147
  {
@@ -136,7 +158,7 @@ describe VagrantPlugins::Openstack::NovaClient do
136
158
  flavor_ref: 'flav',
137
159
  networks: nil,
138
160
  keypair: 'key',
139
- security_groups: ['default'],
161
+ security_groups: [{ name: 'default' }],
140
162
  user_data: 'user_data_test',
141
163
  metadata: 'metadata_test',
142
164
  scheduler_hints: 'sched_hints_test')
@@ -145,7 +167,7 @@ describe VagrantPlugins::Openstack::NovaClient do
145
167
  end
146
168
  end
147
169
 
148
- context 'with one two networks' do
170
+ context 'with two networks' do
149
171
  it 'returns new instance id' do
150
172
 
151
173
  stub_request(:post, 'http://nova/a1b2c3/servers')
@@ -159,7 +181,8 @@ describe VagrantPlugins::Openstack::NovaClient do
159
181
  })
160
182
  .to_return(status: 202, body: '{ "server": { "id": "o1o2o3" } }')
161
183
 
162
- instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav', networks: %w(net1 net2), keypair: 'key')
184
+ instance_id = @nova_client.create_server(env, name: 'inst', image_ref: 'img', flavor_ref: 'flav',
185
+ networks: [{ uuid: 'net1' }, { uuid: 'net2' }], keypair: 'key')
163
186
 
164
187
  expect(instance_id).to eq('o1o2o3')
165
188
  end
@@ -230,7 +253,7 @@ describe VagrantPlugins::Openstack::NovaClient do
230
253
  it 'returns newly created keypair name' do
231
254
  File.should_receive(:exist?).with(filename).and_return(true)
232
255
  File.should_receive(:open).with(filename).and_return(file)
233
- Kernel.stub!(:rand).and_return(2_036_069_739_008)
256
+ Kernel.stub(:rand).and_return(2_036_069_739_008)
234
257
 
235
258
  stub_request(:post, 'http://nova/a1b2c3/os-keypairs')
236
259
  .with(
@@ -413,7 +436,8 @@ describe VagrantPlugins::Openstack::NovaClient do
413
436
 
414
437
  floating_ips = @nova_client.get_all_floating_ips(env)
415
438
 
416
- expect(floating_ips).to have(2).items
439
+ expect(floating_ips).not_to be_nil
440
+ expect(floating_ips.size).to eq(2)
417
441
  expect(floating_ips[0].ip).to eql('185.39.216.45')
418
442
  expect(floating_ips[0].instance_id).to eql('1234')
419
443
  expect(floating_ips[0].pool).to eql('PublicNetwork-01')
@@ -663,4 +687,33 @@ describe VagrantPlugins::Openstack::NovaClient do
663
687
  end
664
688
  end
665
689
  end
690
+
691
+ describe 'attach_volume' do
692
+ context 'with token and project_id acquainted' do
693
+ context 'with volume id and device' do
694
+ it 'call the nova api' do
695
+ stub_request(:post, 'http://nova/a1b2c3/servers/9876/os-volume_attachments')
696
+ .with(headers:
697
+ {
698
+ 'Accept' => 'application/json',
699
+ 'X-Auth-Token' => '123456'
700
+ },
701
+ body: '{"volumeAttachment":{"volumeId":"n1n2","device":"/dev/vdg"}}')
702
+ .to_return(status: 200, body: '
703
+ {
704
+ "volumeAttachment": {
705
+ "device": "/dev/vdg",
706
+ "id": "attachment-01",
707
+ "serverId": "9876",
708
+ "volumeId": "n1n2"
709
+ }
710
+ }
711
+ ')
712
+
713
+ attachment = @nova_client.attach_volume(env, '9876', 'n1n2', '/dev/vdg')
714
+ expect(attachment['id']).to eq('attachment-01')
715
+ end
716
+ end
717
+ end
718
+ end
666
719
  end
@@ -128,7 +128,7 @@ describe VagrantPlugins::Openstack::HttpUtils do
128
128
  end
129
129
 
130
130
  context 'response code is 404' do
131
- it 'should return raise a VagrantOpenstackError with conflict message' do
131
+ it 'should raise a VagrantOpenstackError with conflict message' do
132
132
  mock_resp = double.tap do |mock|
133
133
  mock.stub(:code).and_return(404)
134
134
  mock.stub(:headers)
@@ -0,0 +1,44 @@
1
+ require 'vagrant-openstack-provider/spec_helper'
2
+
3
+ describe VagrantPlugins::Openstack::Command::FlavorList do
4
+ describe 'cmd' do
5
+
6
+ let(:nova) do
7
+ double('nova').tap do |nova|
8
+ nova.stub(:get_all_flavors) do
9
+ [
10
+ Flavor.new('001', 'small', '1', '1024', '10'),
11
+ Flavor.new('002', 'large', '4', '4096', '100')
12
+ ]
13
+ end
14
+ end
15
+ end
16
+
17
+ let(:env) do
18
+ Hash.new.tap do |env|
19
+ env[:ui] = double('ui')
20
+ env[:ui].stub(:info).with(anything)
21
+ env[:openstack_client] = double
22
+ env[:openstack_client].stub(:nova) { nova }
23
+ end
24
+ end
25
+
26
+ before :each do
27
+ @flavor_list_cmd = VagrantPlugins::Openstack::Command::FlavorList.new(nil, env)
28
+ end
29
+
30
+ it 'prints flovor list from server' do
31
+ nova.should_receive(:get_all_flavors).with(env)
32
+
33
+ expect(env[:ui]).to receive(:info).with('
34
+ +-----+-------+------+----------+----------------+
35
+ | Id | Name | vCPU | RAM (Mo) | Disk size (Go) |
36
+ +-----+-------+------+----------+----------------+
37
+ | 001 | small | 1 | 1024 | 10 |
38
+ | 002 | large | 4 | 4096 | 100 |
39
+ +-----+-------+------+----------+----------------+')
40
+
41
+ @flavor_list_cmd.cmd('flavor-list', [], env)
42
+ end
43
+ end
44
+ end
@@ -49,10 +49,27 @@ describe VagrantPlugins::Openstack::Command::FloatingIpList do
49
49
  @floating_ip_list_cmd = VagrantPlugins::Openstack::Command::FloatingIpList.new(nil, env)
50
50
  end
51
51
 
52
- it 'should get floating ip and floating ip pool from server' do
52
+ it 'prints floating ip and floating ip pool from server' do
53
53
  nova.should_receive(:get_floating_ip_pools).with(env)
54
54
  nova.should_receive(:get_floating_ips).with(env)
55
- @floating_ip_list_cmd.cmd('floatingip-list', ['--'], env)
55
+
56
+ expect(env[:ui]).to receive(:info).with('
57
+ +-------------------+
58
+ | Floating IP pools |
59
+ +-------------------+
60
+ | pool1 |
61
+ | pool2 |
62
+ +-------------------+').ordered
63
+
64
+ expect(env[:ui]).to receive(:info).with('
65
+ +----+------------+-------+-------------+
66
+ | Id | IP | Pool | Instance id |
67
+ +----+------------+-------+-------------+
68
+ | 1 | 10.10.10.1 | pool1 | |
69
+ | 2 | 10.10.10.2 | pool2 | inst001 |
70
+ +----+------------+-------+-------------+').ordered
71
+
72
+ @floating_ip_list_cmd.cmd('floatingip-list', [], env)
56
73
  end
57
74
  end
58
75
  end
@@ -0,0 +1,48 @@
1
+ require 'vagrant-openstack-provider/spec_helper'
2
+
3
+ describe VagrantPlugins::Openstack::Command::ImageList do
4
+ describe 'cmd' do
5
+
6
+ let(:nova) do
7
+ double('nova').tap do |nova|
8
+ nova.stub(:get_all_images) do
9
+ [
10
+ Item.new('0001', 'ubuntu'),
11
+ Item.new('0002', 'centos'),
12
+ Item.new('0003', 'debian')
13
+ ]
14
+ end
15
+ end
16
+ end
17
+
18
+ let(:env) do
19
+ Hash.new.tap do |env|
20
+ env[:ui] = double('ui')
21
+ env[:ui].stub(:info).with(anything)
22
+ env[:openstack_client] = double
23
+ env[:openstack_client].stub(:nova) { nova }
24
+ end
25
+ end
26
+
27
+ before :each do
28
+ @image_list_cmd = VagrantPlugins::Openstack::Command::ImageList.new(['--'], env)
29
+ end
30
+
31
+ it 'prints image list from server' do
32
+
33
+ allow(@image_list_cmd).to receive(:with_target_vms).and_return(nil)
34
+
35
+ nova.should_receive(:get_all_images).with(env)
36
+
37
+ expect(env[:ui]).to receive(:info).with('
38
+ +------+--------+
39
+ | Id | Name |
40
+ +------+--------+
41
+ | 0001 | ubuntu |
42
+ | 0002 | centos |
43
+ | 0003 | debian |
44
+ +------+--------+')
45
+ @image_list_cmd.cmd('image-list', [], env)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,67 @@
1
+ require 'vagrant-openstack-provider/spec_helper'
2
+
3
+ describe VagrantPlugins::Openstack::Command::NetworkList do
4
+ describe 'cmd' do
5
+
6
+ let(:neutron) do
7
+ double('neutron').tap do |neutron|
8
+ neutron.stub(:get_private_networks) do
9
+ [
10
+ Item.new('net-01', 'internal'),
11
+ Item.new('net-02', 'external')
12
+ ]
13
+ end
14
+ neutron.stub(:get_all_networks) do
15
+ [
16
+ Item.new('pub-01', 'public'),
17
+ Item.new('net-01', 'internal'),
18
+ Item.new('net-02', 'external')
19
+ ]
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:env) do
25
+ Hash.new.tap do |env|
26
+ env[:ui] = double('ui')
27
+ env[:ui].stub(:info).with(anything)
28
+ env[:openstack_client] = double
29
+ env[:openstack_client].stub(:neutron) { neutron }
30
+ end
31
+ end
32
+
33
+ before :each do
34
+ @network_list_cmd = VagrantPlugins::Openstack::Command::NetworkList.new(nil, env)
35
+ end
36
+
37
+ it 'prints network list from server' do
38
+ neutron.should_receive(:get_private_networks).with(env)
39
+
40
+ expect(env[:ui]).to receive(:info).with('
41
+ +--------+----------+
42
+ | Id | Name |
43
+ +--------+----------+
44
+ | net-01 | internal |
45
+ | net-02 | external |
46
+ +--------+----------+')
47
+
48
+ @network_list_cmd.cmd('network-list', [], env)
49
+ end
50
+
51
+ it 'prints all networks list from server' do
52
+ neutron.should_receive(:get_all_networks).with(env)
53
+
54
+ expect(env[:ui]).to receive(:info).with('
55
+ +--------+----------+
56
+ | Id | Name |
57
+ +--------+----------+
58
+ | pub-01 | public |
59
+ | net-01 | internal |
60
+ | net-02 | external |
61
+ +--------+----------+')
62
+
63
+ @network_list_cmd.cmd('network-list', ['all'], env)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,25 @@
1
+ require 'vagrant-openstack-provider/spec_helper'
2
+
3
+ describe VagrantPlugins::Openstack::Command::Reset do
4
+ describe 'cmd' do
5
+
6
+ let(:env) do
7
+ Hash.new.tap do |env|
8
+ env[:ui] = double('ui')
9
+ env[:ui].stub(:info).with(anything)
10
+ env[:machine] = double('machine')
11
+ env[:machine].stub(:data_dir) { '/my/data/dir' }
12
+ end
13
+ end
14
+
15
+ before :each do
16
+ @reset_cmd = VagrantPlugins::Openstack::Command::Reset.new(nil, env)
17
+ end
18
+
19
+ it 'resets vagrant openstack machines' do
20
+ expect(env[:ui]).to receive(:info).with('Vagrant OpenStack Provider has been reset')
21
+ expect(FileUtils).to receive(:remove_dir).with('/my/data/dir')
22
+ @reset_cmd.cmd('reset', [], env)
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  require 'vagrant-openstack-provider/spec_helper'
2
2
 
3
- describe VagrantPlugins::Openstack::Command::FloatingIpList do
3
+ describe VagrantPlugins::Openstack::Command::VolumeList do
4
4
  describe 'cmd' do
5
5
 
6
6
  let(:cinder) do
@@ -25,8 +25,16 @@ describe VagrantPlugins::Openstack::Command::FloatingIpList do
25
25
  @volume_list_cmd = VagrantPlugins::Openstack::Command::VolumeList.new(nil, env)
26
26
  end
27
27
 
28
- it 'should get volumes list from server' do
28
+ it 'prints volumes list from server' do
29
29
  cinder.should_receive(:get_all_volumes).with(env)
30
+ expect(env[:ui]).to receive(:info).with('
31
+ +-----+--------+-----------+-----------+-------------------------------------+
32
+ | Id | Name | Size (Go) | Status | Attachment (instance id and device) |
33
+ +-----+--------+-----------+-----------+-------------------------------------+
34
+ | 987 | vol-01 | 2 | available | |
35
+ | 654 | vol-02 | 4 | in-use | inst-01 (/dev/vdc) |
36
+ +-----+--------+-----------+-----------+-------------------------------------+')
37
+
30
38
  @volume_list_cmd.cmd('volume-list', [], env)
31
39
  end
32
40
  end
@@ -0,0 +1,680 @@
1
+ require 'vagrant-openstack-provider/spec_helper'
2
+
3
+ describe VagrantPlugins::Openstack::ConfigResolver do
4
+
5
+ let(:config) do
6
+ double('config').tap do |config|
7
+ config.stub(:tenant_name) { 'testTenant' }
8
+ config.stub(:server_name) { 'testName' }
9
+ config.stub(:floating_ip) { nil }
10
+ config.stub(:floating_ip_pool) { nil }
11
+ config.stub(:floating_ip_pool_always_allocate) { false }
12
+ config.stub(:keypair_name) { nil }
13
+ config.stub(:public_key_path) { nil }
14
+ config.stub(:networks) { nil }
15
+ config.stub(:volumes) { nil }
16
+ end
17
+ end
18
+
19
+ let(:ssh_key) do
20
+ double('ssh_key').tap do |key|
21
+ key.stub(:ssh_public_key) { 'ssh public key' }
22
+ key.stub(:private_key) { 'private key' }
23
+ end
24
+ end
25
+
26
+ let(:neutron) do
27
+ double('neutron').tap do |neutron|
28
+ neutron.stub(:get_private_networks).with(anything) do
29
+ [Item.new('001', 'net-01'),
30
+ Item.new('002', 'net-02'),
31
+ Item.new('003', 'net-03'),
32
+ Item.new('004', 'net-04'),
33
+ Item.new('005', 'net-05'),
34
+ Item.new('006', 'net-06'),
35
+ Item.new('007', 'net-07-08'),
36
+ Item.new('008', 'net-07-08')]
37
+ end
38
+ end
39
+ end
40
+
41
+ let(:nova) do
42
+ double('nova').tap do |nova|
43
+ nova.stub(:get_all_floating_ips).with(anything) do
44
+ [FloatingIP.new('80.81.82.83', 'pool-1', nil), FloatingIP.new('30.31.32.33', 'pool-2', '1234')]
45
+ end
46
+ end
47
+ end
48
+
49
+ let(:cinder) do
50
+ double('cinder').tap do |cinder|
51
+ cinder.stub(:get_all_volumes).with(anything) do
52
+ [Volume.new('001', 'vol-01', '1', 'available', 'true', nil, nil),
53
+ Volume.new('002', 'vol-02', '2', 'available', 'true', nil, nil),
54
+ Volume.new('003', 'vol-03', '3', 'available', 'true', nil, nil),
55
+ Volume.new('004', 'vol-04', '4', 'available', 'false', nil, nil),
56
+ Volume.new('005', 'vol-05', '5', 'available', 'false', nil, nil),
57
+ Volume.new('006', 'vol-06', '6', 'available', 'false', nil, nil),
58
+ Volume.new('007', 'vol-07-08', '6', 'available', 'false', nil, nil),
59
+ Volume.new('008', 'vol-07-08', '6', 'available', 'false', nil, nil)]
60
+ end
61
+ end
62
+ end
63
+
64
+ let(:session) do
65
+ double('session').tap do |s|
66
+ s.stub(:endpoints) do
67
+ {
68
+ identity: 'http://keystone',
69
+ compute: 'http://nova',
70
+ volume: 'http://cinder',
71
+ network: 'http://neutron'
72
+ }
73
+ end
74
+ end
75
+ end
76
+
77
+ let(:env) do
78
+ Hash.new.tap do |env|
79
+ env[:ui] = double('ui')
80
+ env[:ui].stub(:info).with(anything)
81
+ env[:machine] = double('machine')
82
+ env[:machine].stub(:provider_config) { config }
83
+ env[:machine].stub(:data_dir) { '/data/dir' }
84
+ env[:machine].stub(:config) { machine_config }
85
+ env[:openstack_client] = double('openstack_client')
86
+ env[:openstack_client].stub(:neutron) { neutron }
87
+ env[:openstack_client].stub(:nova) { nova }
88
+ env[:openstack_client].stub(:cinder) { cinder }
89
+ env[:openstack_client].stub(:session) { session }
90
+ end
91
+ end
92
+
93
+ let(:ssh_config) do
94
+ double('ssh_config').tap do |config|
95
+ config.stub(:username) { nil }
96
+ config.stub(:port) { nil }
97
+ end
98
+ end
99
+
100
+ let(:machine_config) do
101
+ double('machine_config').tap do |config|
102
+ config.stub(:ssh) { ssh_config }
103
+ end
104
+ end
105
+
106
+ before :each do
107
+ ConfigResolver.send(:public, *ConfigResolver.private_instance_methods)
108
+ @action = ConfigResolver.new
109
+ end
110
+
111
+ describe 'resolve_ssh_username' do
112
+ context 'with machine.ssh.username' do
113
+ it 'returns machine.ssh.username' do
114
+ ssh_config.stub(:username) { 'machine ssh username' }
115
+ config.stub(:ssh_username) { nil }
116
+ expect(@action.resolve_ssh_username(env)).to eq('machine ssh username')
117
+ end
118
+ end
119
+ context 'with machine.ssh.username and config.ssh_username' do
120
+ it 'returns machine.ssh.username' do
121
+ ssh_config.stub(:username) { 'machine ssh username' }
122
+ config.stub(:ssh_username) { 'provider ssh username' }
123
+ expect(@action.resolve_ssh_username(env)).to eq('machine ssh username')
124
+ end
125
+ end
126
+ context 'with config.ssh_username' do
127
+ it 'returns config.ssh_username' do
128
+ ssh_config.stub(:username) { nil }
129
+ config.stub(:ssh_username) { 'provider ssh username' }
130
+ expect(@action.resolve_ssh_username(env)).to eq('provider ssh username')
131
+ end
132
+ end
133
+ context 'with no ssh username config' do
134
+ it 'fails' do
135
+ ssh_config.stub(:username) { nil }
136
+ config.stub(:ssh_username) { nil }
137
+ expect { @action.resolve_ssh_username(env) }.to raise_error(Errors::NoMatchingSshUsername)
138
+ end
139
+ end
140
+ end
141
+
142
+ describe 'resolve_flavor' do
143
+ context 'with id' do
144
+ it 'returns the specified flavor' do
145
+ config.stub(:flavor) { 'fl-001' }
146
+ nova.stub(:get_all_flavors).with(anything) do
147
+ [Flavor.new('fl-001', 'flavor-01', 2, 1024, 10),
148
+ Flavor.new('fl-002', 'flavor-02', 4, 2048, 50)]
149
+ end
150
+ @action.resolve_flavor(env).should eq(Flavor.new('fl-001', 'flavor-01', 2, 1024, 10))
151
+ end
152
+ end
153
+ context 'with name' do
154
+ it 'returns the specified flavor' do
155
+ config.stub(:flavor) { 'flavor-02' }
156
+ nova.stub(:get_all_flavors).with(anything) do
157
+ [Flavor.new('fl-001', 'flavor-01', 2, 1024, 10),
158
+ Flavor.new('fl-002', 'flavor-02', 4, 2048, 50)]
159
+ end
160
+ @action.resolve_flavor(env).should eq(Flavor.new('fl-002', 'flavor-02', 4, 2048, 50))
161
+ end
162
+ end
163
+ context 'with invalid identifier' do
164
+ it 'raise an error' do
165
+ config.stub(:flavor) { 'not-existing' }
166
+ nova.stub(:get_all_flavors).with(anything) do
167
+ [Flavor.new('fl-001', 'flavor-01', 2, 1024, 10),
168
+ Flavor.new('fl-002', 'flavor-02', 4, 2048, 50)]
169
+ end
170
+ expect { @action.resolve_flavor(env) }.to raise_error(Errors::NoMatchingFlavor)
171
+ end
172
+ end
173
+ end
174
+
175
+ describe 'resolve_image' do
176
+ context 'with id' do
177
+ it 'returns the specified flavor' do
178
+ config.stub(:image) { 'img-001' }
179
+ nova.stub(:get_all_images).with(anything) do
180
+ [Item.new('img-001', 'image-01'),
181
+ Item.new('img-002', 'image-02')]
182
+ end
183
+ @action.resolve_image(env).should eq(Item.new('img-001', 'image-01'))
184
+ end
185
+ end
186
+ context 'with name' do
187
+ it 'returns the specified flavor' do
188
+ config.stub(:image) { 'image-02' }
189
+ nova.stub(:get_all_images).with(anything) do
190
+ [Item.new('img-001', 'image-01'),
191
+ Item.new('img-002', 'image-02')]
192
+ end
193
+ @action.resolve_image(env).should eq(Item.new('img-002', 'image-02'))
194
+ end
195
+ end
196
+ context 'with invalid identifier' do
197
+ it 'raise an error' do
198
+ config.stub(:image) { 'not-existing' }
199
+ nova.stub(:get_all_images).with(anything) do
200
+ [Item.new('img-001', 'image-01'),
201
+ Item.new('img-002', 'image-02')]
202
+ end
203
+ expect { @action.resolve_image(env) }.to raise_error(Errors::NoMatchingImage)
204
+ end
205
+ end
206
+ end
207
+
208
+ describe 'resolve_floating_ip' do
209
+ context 'with config.floating_ip specified' do
210
+ it 'return the specified floating ip' do
211
+ config.stub(:floating_ip) { '80.80.80.80' }
212
+ @action.resolve_floating_ip(env).should eq('80.80.80.80')
213
+ end
214
+ end
215
+
216
+ context 'with config.floating_pool specified' do
217
+ context 'if any ip in the same pool is available' do
218
+ context 'with config.floating_pool_always_allocate true' do
219
+ it 'allocate a new floating_ip from the pool' do
220
+ config.stub(:floating_ip_pool_always_allocate) { true }
221
+ nova.stub(:get_all_floating_ips).with(anything) do
222
+ [FloatingIP.new('80.81.82.84', 'pool-1', '1234'),
223
+ FloatingIP.new('80.81.82.83', 'pool-1', nil)]
224
+ end
225
+ nova.stub(:allocate_floating_ip).with(env, 'pool-1') do
226
+ FloatingIP.new('80.81.82.84', 'pool-1', nil)
227
+ end
228
+ config.stub(:floating_ip_pool) { 'pool-1' }
229
+ @action.resolve_floating_ip(env).should eq('80.81.82.84')
230
+ end
231
+ end
232
+
233
+ context 'with config.floating_pool_always_allocate false' do
234
+ it 'return one of the available ips' do
235
+ config.stub(:floating_ip_pool_always_allocate) { false }
236
+ nova.stub(:get_all_floating_ips).with(anything) do
237
+ [FloatingIP.new('80.81.82.84', 'pool-1', '1234'),
238
+ FloatingIP.new('80.81.82.83', 'pool-1', nil)]
239
+ end
240
+ config.stub(:floating_ip_pool) { 'pool-1' }
241
+ @action.resolve_floating_ip(env).should eq('80.81.82.83')
242
+ end
243
+ end
244
+ end
245
+
246
+ context 'if no ip in the same pool is available' do
247
+ it 'allocate a new floating_ip from the pool' do
248
+ nova.stub(:get_all_floating_ips).with(anything) do
249
+ [FloatingIP.new('80.81.82.83', 'pool-1', '1234')]
250
+ end
251
+ nova.stub(:allocate_floating_ip).with(env, 'pool-1') do
252
+ FloatingIP.new('80.81.82.84', 'pool-1', nil)
253
+ end
254
+ config.stub(:floating_ip_pool) { 'pool-1' }
255
+ @action.resolve_floating_ip(env).should eq('80.81.82.84')
256
+ end
257
+ end
258
+ end
259
+
260
+ context 'with neither floating_ip nor floating_ip_pool' do
261
+ it 'fails with an UnableToResolveFloatingIP error' do
262
+ config.stub(:floating_ip) { nil }
263
+ config.stub(:floating_ip_pool) { nil }
264
+ expect { @action.resolve_floating_ip(env) }.to raise_error(Errors::UnableToResolveFloatingIP)
265
+ end
266
+ end
267
+ end
268
+
269
+ describe 'resolve_keypair' do
270
+ context 'with keypair_name provided' do
271
+ it 'return the provided keypair_name' do
272
+ config.stub(:keypair_name) { 'my-keypair' }
273
+ @action.resolve_keypair(env).should eq('my-keypair')
274
+ end
275
+ end
276
+
277
+ context 'with keypair_name and public_key_path provided' do
278
+ it 'return the provided keypair_name' do
279
+ config.stub(:keypair_name) { 'my-keypair' }
280
+ config.stub(:public_key_path) { '/path/to/key' }
281
+ @action.resolve_keypair(env).should eq('my-keypair')
282
+ end
283
+ end
284
+
285
+ context 'with public_key_path provided' do
286
+ it 'return the keypair_name created into nova' do
287
+ config.stub(:public_key_path) { '/path/to/key' }
288
+ nova.stub(:import_keypair_from_file).with(env, '/path/to/key') { 'my-keypair-imported' }
289
+ @action.resolve_keypair(env).should eq('my-keypair-imported')
290
+ end
291
+ end
292
+
293
+ context 'with no keypair_name and no public_key_path provided' do
294
+ it 'generates a new keypair and return the keypair name imported into nova' do
295
+ config.stub(:keypair_name) { nil }
296
+ config.stub(:public_key_path) { nil }
297
+ @action.stub(:generate_keypair) { 'my-keypair-imported' }
298
+ @action.resolve_keypair(env).should eq('my-keypair-imported')
299
+ end
300
+ end
301
+ end
302
+
303
+ describe 'generate_keypair' do
304
+ it 'returns a generated keypair name imported into nova' do
305
+ nova.stub(:import_keypair) { 'my-keypair-imported' }
306
+ SSHKey.stub(:generate) { ssh_key }
307
+ File.should_receive(:write).with('/data/dir/my-keypair-imported', 'private key')
308
+ File.should_receive(:chmod).with(0600, '/data/dir/my-keypair-imported')
309
+ @action.generate_keypair(env).should eq('my-keypair-imported')
310
+ end
311
+ end
312
+
313
+ describe 'resolve_networks' do
314
+ context 'neutron service is available' do
315
+ context 'with network configured in all possible ways' do
316
+ it 'returns normalized network list' do
317
+
318
+ config.stub(:networks) do
319
+ ['001',
320
+ 'net-02',
321
+ { id: '003', address: '1.2.3.4' },
322
+ { name: 'net-04', address: '5.6.7.8' },
323
+ { name: 'net-05' },
324
+ { id: '006' }]
325
+ end
326
+
327
+ expect(@action.resolve_networks(env)).to eq [{ uuid: '001' },
328
+ { uuid: '002' },
329
+ { uuid: '003', fixed_ip: '1.2.3.4' },
330
+ { uuid: '004', fixed_ip: '5.6.7.8' },
331
+ { uuid: '005' },
332
+ { uuid: '006' }]
333
+ end
334
+ end
335
+
336
+ context 'with invalid network object' do
337
+ it 'raises an error' do
338
+ config.stub(:networks) { [1] }
339
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::InvalidNetworkObject)
340
+ end
341
+ end
342
+
343
+ context 'with string that is neither an id nor name matching a network' do
344
+ it 'raises an error' do
345
+ config.stub(:networks) { ['not-exist'] }
346
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::UnresolvedNetwork)
347
+ end
348
+ end
349
+
350
+ context 'with hash containing a bad id' do
351
+ it 'raises an error' do
352
+ config.stub(:networks) { [{ id: 'not-exist' }] }
353
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::UnresolvedNetworkId)
354
+ end
355
+ end
356
+
357
+ context 'with hash containing a bad name' do
358
+ it 'raises an error' do
359
+ config.stub(:networks) { [{ name: 'not-exist' }] }
360
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::UnresolvedNetworkName)
361
+ end
362
+ end
363
+
364
+ context 'with empty hash' do
365
+ it 'raises an error' do
366
+ config.stub(:networks) { [{}] }
367
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::ConflictNetworkNameId)
368
+ end
369
+ end
370
+
371
+ context 'with hash containing both id and name' do
372
+ it 'raises an error' do
373
+ config.stub(:networks) { [{ id: '001', name: 'net-01' }] }
374
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::ConflictNetworkNameId)
375
+ end
376
+ end
377
+
378
+ context 'with hash containing a name matching more than one network' do
379
+ it 'raises an error' do
380
+ config.stub(:networks) { [{ name: 'net-07-08' }] }
381
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::MultipleNetworkName)
382
+ end
383
+ end
384
+ end
385
+
386
+ context 'neutron service is not available' do
387
+ context 'with network configured in all possible ways' do
388
+ it 'returns normalized network list' do
389
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
390
+ config.stub(:networks) do
391
+ ['001',
392
+ { id: '003', address: '1.2.3.4' },
393
+ { id: '006' }]
394
+ end
395
+
396
+ expect(@action.resolve_networks(env)).to eq [{ uuid: '001' },
397
+ { uuid: '003', fixed_ip: '1.2.3.4' },
398
+ { uuid: '006' }]
399
+ end
400
+ end
401
+
402
+ context 'with hash containing both id and name' do
403
+ it 'raises an error' do
404
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
405
+ config.stub(:networks) { [{ id: '001', name: 'net-01' }] }
406
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::ConflictNetworkNameId)
407
+ end
408
+ end
409
+
410
+ context 'with hash containing name' do
411
+ it 'raises an error' do
412
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
413
+ config.stub(:networks) { [{ name: 'net-01' }] }
414
+ expect { @action.resolve_networks(env) }.to raise_error(Errors::NetworkServiceUnavailable)
415
+ end
416
+ end
417
+ end
418
+ end
419
+
420
+ describe 'resolve_volume_boot' do
421
+ context 'cinder service is available' do
422
+ context 'with string volume id' do
423
+ it 'returns normalized volume' do
424
+ config.stub(:volume_boot) { '001' }
425
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
426
+ end
427
+ end
428
+
429
+ context 'with string volume name' do
430
+ it 'returns normalized volume' do
431
+ config.stub(:volume_boot) { 'vol-01' }
432
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
433
+ end
434
+ end
435
+
436
+ context 'with hash volume id' do
437
+ it 'returns normalized volume' do
438
+ config.stub(:volume_boot) { { id: '001' } }
439
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
440
+ end
441
+ end
442
+
443
+ context 'with hash volume name' do
444
+ it 'returns normalized volume' do
445
+ config.stub(:volume_boot) { { name: 'vol-01' } }
446
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
447
+ end
448
+ end
449
+
450
+ context 'with hash volume id and device' do
451
+ it 'returns normalized volume' do
452
+ config.stub(:volume_boot) { { id: '001', device: 'vdb' } }
453
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vdb'
454
+ end
455
+ end
456
+
457
+ context 'with hash volume name and device' do
458
+ it 'returns normalized volume' do
459
+ config.stub(:volume_boot) { { name: 'vol-01', device: 'vdb' } }
460
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vdb'
461
+ end
462
+ end
463
+
464
+ context 'with empty hash' do
465
+ it 'raises an error' do
466
+ config.stub(:volume_boot) { {} }
467
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::ConflictVolumeNameId)
468
+ end
469
+ end
470
+
471
+ context 'with invalid volume object' do
472
+ it 'raises an error' do
473
+ config.stub(:volume_boot) { 1 }
474
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::InvalidVolumeObject)
475
+ end
476
+ end
477
+
478
+ context 'with hash containing a bad id' do
479
+ it 'raises an error' do
480
+ config.stub(:volume_boot) { { id: 'not-exist' } }
481
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::UnresolvedVolumeId)
482
+ end
483
+ end
484
+
485
+ context 'with hash containing a bad name' do
486
+ it 'raises an error' do
487
+ config.stub(:volume_boot) { { name: 'not-exist' } }
488
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::UnresolvedVolumeName)
489
+ end
490
+ end
491
+
492
+ context 'with hash containing both id and name' do
493
+ it 'raises an error' do
494
+ config.stub(:volume_boot) { { id: '001', name: 'vol-01' } }
495
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::ConflictVolumeNameId)
496
+ end
497
+ end
498
+
499
+ context 'with hash containing a name matching more than one volume' do
500
+ it 'raises an error' do
501
+ config.stub(:volume_boot) { { name: 'vol-07-08' } }
502
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::MultipleVolumeName)
503
+ end
504
+ end
505
+ end
506
+
507
+ context 'cinder service is not available' do
508
+ context 'with string volume id' do
509
+ it 'returns normalized volume' do
510
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
511
+ config.stub(:volume_boot) { '001' }
512
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
513
+ end
514
+ end
515
+
516
+ context 'with hash volume id' do
517
+ it 'returns normalized volume' do
518
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
519
+ config.stub(:volume_boot) { { id: '001' } }
520
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vda'
521
+ end
522
+ end
523
+
524
+ context 'with hash volume name' do
525
+ it 'raise an error' do
526
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
527
+ config.stub(:volume_boot) { { name: 'vol-01' } }
528
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::VolumeServiceUnavailable)
529
+ end
530
+ end
531
+
532
+ context 'with hash volume id and device' do
533
+ it 'returns normalized volume' do
534
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
535
+ config.stub(:volume_boot) { { id: '001', device: 'vdb' } }
536
+ expect(@action.resolve_volume_boot(env)).to eq id: '001', device: 'vdb'
537
+ end
538
+ end
539
+
540
+ context 'with hash volume name and device' do
541
+ it 'raise an error' do
542
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
543
+ config.stub(:volume_boot) { { name: 'vol-01', device: 'vdb' } }
544
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::VolumeServiceUnavailable)
545
+ end
546
+ end
547
+
548
+ context 'with invalid volume object' do
549
+ it 'raises an error' do
550
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
551
+ config.stub(:volume_boot) { 1 }
552
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::InvalidVolumeObject)
553
+ end
554
+ end
555
+
556
+ context 'with hash containing both id and name' do
557
+ it 'raises an error' do
558
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
559
+ config.stub(:volume_boot) { { id: '001', name: 'vol-01' } }
560
+ expect { @action.resolve_volume_boot(env) }.to raise_error(Errors::ConflictVolumeNameId)
561
+ end
562
+ end
563
+ end
564
+ end
565
+
566
+ describe 'resolve_volumes' do
567
+ context 'cinder service is available' do
568
+ context 'with volume attached in all possible ways' do
569
+ it 'returns normalized volume list' do
570
+
571
+ config.stub(:volumes) do
572
+ ['001',
573
+ 'vol-02',
574
+ { id: '003', device: '/dev/vdz' },
575
+ { name: 'vol-04', device: '/dev/vdy' },
576
+ { name: 'vol-05' },
577
+ { id: '006' }]
578
+ end
579
+
580
+ expect(@action.resolve_volumes(env)).to eq [{ id: '001', device: nil },
581
+ { id: '002', device: nil },
582
+ { id: '003', device: '/dev/vdz' },
583
+ { id: '004', device: '/dev/vdy' },
584
+ { id: '005', device: nil },
585
+ { id: '006', device: nil }]
586
+ end
587
+ end
588
+
589
+ context 'with invalid volume object' do
590
+ it 'raises an error' do
591
+ config.stub(:volumes) { [1] }
592
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::InvalidVolumeObject)
593
+ end
594
+ end
595
+
596
+ context 'with string that is neither an id nor name matching a volume' do
597
+ it 'raises an error' do
598
+ config.stub(:volumes) { ['not-exist'] }
599
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolume)
600
+ end
601
+ end
602
+
603
+ context 'with hash containing a bad id' do
604
+ it 'raises an error' do
605
+ config.stub(:volumes) { [{ id: 'not-exist' }] }
606
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolumeId)
607
+ end
608
+ end
609
+
610
+ context 'with hash containing a bad name' do
611
+ it 'raises an error' do
612
+ config.stub(:volumes) { [{ name: 'not-exist' }] }
613
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::UnresolvedVolumeName)
614
+ end
615
+ end
616
+
617
+ context 'with empty hash' do
618
+ it 'raises an error' do
619
+ config.stub(:volumes) { [{}] }
620
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
621
+ end
622
+ end
623
+
624
+ context 'with hash containing both id and name' do
625
+ it 'raises an error' do
626
+ config.stub(:volumes) { [{ id: '001', name: 'vol-01' }] }
627
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
628
+ end
629
+ end
630
+
631
+ context 'with hash containing a name matching more than one volume' do
632
+ it 'raises an error' do
633
+ config.stub(:volumes) { [{ name: 'vol-07-08' }] }
634
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::MultipleVolumeName)
635
+ end
636
+ end
637
+ end
638
+
639
+ context 'cinder service is not available' do
640
+ context 'with volume attached in all possible ways' do
641
+ it 'returns normalized volume list' do
642
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
643
+ config.stub(:volumes) do
644
+ ['001',
645
+ { id: '003', device: '/dev/vdz' },
646
+ { id: '006' }]
647
+ end
648
+
649
+ expect(@action.resolve_volumes(env)).to eq [{ id: '001', device: nil },
650
+ { id: '003', device: '/dev/vdz' },
651
+ { id: '006', device: nil }]
652
+ end
653
+
654
+ context 'with hash containing both id and name' do
655
+ it 'raises an error' do
656
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
657
+ config.stub(:volumes) { [{ id: '001', name: 'vol-01' }] }
658
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::ConflictVolumeNameId)
659
+ end
660
+ end
661
+ context 'with hash containing name' do
662
+ it 'raises an error' do
663
+ session.stub(:endpoints) { { identity: 'http://keystone', compute: 'http://nova' } }
664
+ config.stub(:volumes) { [{ name: 'vol-01' }] }
665
+ expect { @action.resolve_volumes(env) }.to raise_error(Errors::VolumeServiceUnavailable)
666
+ end
667
+ end
668
+ end
669
+ end
670
+ end
671
+
672
+ describe 'resolve_security_groups' do
673
+ context 'with Hash and String objects' do
674
+ it 'returns normalized Hash list' do
675
+ config.stub(:security_groups) { ['group1', { name: 'group2' }] }
676
+ expect(@action.resolve_security_groups(env)).to eq([{ name: 'group1' }, { name: 'group2' }])
677
+ end
678
+ end
679
+ end
680
+ end