foreman_google 3.0.8 → 3.1.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_google/locale/cs_CZ/foreman_google.js +7 -1
  3. data/app/assets/javascripts/foreman_google/locale/de/foreman_google.js +7 -1
  4. data/app/assets/javascripts/foreman_google/locale/el/foreman_google.js +7 -1
  5. data/app/assets/javascripts/foreman_google/locale/en/foreman_google.js +7 -1
  6. data/app/assets/javascripts/foreman_google/locale/en_GB/foreman_google.js +7 -1
  7. data/app/assets/javascripts/foreman_google/locale/es/foreman_google.js +7 -1
  8. data/app/assets/javascripts/foreman_google/locale/fr/foreman_google.js +8 -2
  9. data/app/assets/javascripts/foreman_google/locale/it/foreman_google.js +7 -1
  10. data/app/assets/javascripts/foreman_google/locale/ja/foreman_google.js +8 -2
  11. data/app/assets/javascripts/foreman_google/locale/ka/foreman_google.js +7 -1
  12. data/app/assets/javascripts/foreman_google/locale/ko/foreman_google.js +8 -2
  13. data/app/assets/javascripts/foreman_google/locale/pl/foreman_google.js +7 -1
  14. data/app/assets/javascripts/foreman_google/locale/pt_BR/foreman_google.js +7 -1
  15. data/app/assets/javascripts/foreman_google/locale/ru/foreman_google.js +7 -1
  16. data/app/assets/javascripts/foreman_google/locale/sv_SE/foreman_google.js +7 -1
  17. data/app/assets/javascripts/foreman_google/locale/zh_CN/foreman_google.js +8 -2
  18. data/app/assets/javascripts/foreman_google/locale/zh_TW/foreman_google.js +7 -1
  19. data/app/lib/google_cloud_compute/compute_attributes.rb +3 -1
  20. data/app/models/foreman_google/gce.rb +2 -4
  21. data/app/models/foreman_google/google_compute.rb +2 -2
  22. data/lib/foreman_google/engine.rb +1 -1
  23. data/lib/foreman_google/version.rb +1 -1
  24. data/locale/cs_CZ/LC_MESSAGES/foreman_google.mo +0 -0
  25. data/locale/cs_CZ/foreman_google.po +7 -1
  26. data/locale/de/LC_MESSAGES/foreman_google.mo +0 -0
  27. data/locale/de/foreman_google.po +7 -1
  28. data/locale/el/LC_MESSAGES/foreman_google.mo +0 -0
  29. data/locale/el/foreman_google.po +7 -1
  30. data/locale/en/LC_MESSAGES/foreman_google.mo +0 -0
  31. data/locale/en/foreman_google.po +7 -1
  32. data/locale/en_GB/LC_MESSAGES/foreman_google.mo +0 -0
  33. data/locale/en_GB/foreman_google.po +7 -1
  34. data/locale/es/LC_MESSAGES/foreman_google.mo +0 -0
  35. data/locale/es/foreman_google.po +7 -1
  36. data/locale/foreman_google.pot +12 -4
  37. data/locale/fr/LC_MESSAGES/foreman_google.mo +0 -0
  38. data/locale/fr/foreman_google.po +9 -3
  39. data/locale/it/LC_MESSAGES/foreman_google.mo +0 -0
  40. data/locale/it/foreman_google.po +7 -1
  41. data/locale/ja/LC_MESSAGES/foreman_google.mo +0 -0
  42. data/locale/ja/foreman_google.po +9 -3
  43. data/locale/ka/LC_MESSAGES/foreman_google.mo +0 -0
  44. data/locale/ka/foreman_google.po +7 -1
  45. data/locale/ko/LC_MESSAGES/foreman_google.mo +0 -0
  46. data/locale/ko/foreman_google.po +9 -3
  47. data/locale/pl/LC_MESSAGES/foreman_google.mo +0 -0
  48. data/locale/pl/foreman_google.po +7 -1
  49. data/locale/pt_BR/LC_MESSAGES/foreman_google.mo +0 -0
  50. data/locale/pt_BR/foreman_google.po +7 -1
  51. data/locale/ru/LC_MESSAGES/foreman_google.mo +0 -0
  52. data/locale/ru/foreman_google.po +7 -1
  53. data/locale/sv_SE/LC_MESSAGES/foreman_google.mo +0 -0
  54. data/locale/sv_SE/foreman_google.po +7 -1
  55. data/locale/zh_CN/LC_MESSAGES/foreman_google.mo +0 -0
  56. data/locale/zh_CN/foreman_google.po +9 -3
  57. data/locale/zh_TW/LC_MESSAGES/foreman_google.mo +0 -0
  58. data/locale/zh_TW/foreman_google.po +7 -1
  59. data/test/controllers/foreman_google/api/v2/compute_resources_extensions_test.rb +71 -0
  60. data/test/models/concerns/foreman_google/host_managed_extensions_test.rb +46 -0
  61. data/test/models/foreman_google/gce_test.rb +275 -3
  62. data/test/models/foreman_google/google_compute_test.rb +193 -4
  63. data/test/unit/foreman_google/google_compute_adapter_test.rb +59 -7
  64. data/test/unit/google_cloud_compute/compute_attributes_test.rb +235 -0
  65. data/test/unit/google_cloud_compute/compute_collection_test.rb +78 -0
  66. data/test/unit/google_extensions/attached_disk_test.rb +35 -0
  67. metadata +10 -2
@@ -2,7 +2,7 @@ require 'test_google_helper'
2
2
 
3
3
  module ForemanGoogle
4
4
  class GCETest < GoogleTestCase
5
- subject { ForemanGoogle::GCE.new(zone: 'zone', password: gauth_json) }
5
+ subject { ForemanGoogle::GCE.new(zone: 'us-east1-b', password: gauth_json) }
6
6
  let(:service) { mock('GoogleAdapter') }
7
7
 
8
8
  let(:instance_args) do
@@ -18,6 +18,142 @@ module ForemanGoogle
18
18
  service.stubs(:project_id).returns('project_id')
19
19
  end
20
20
 
21
+ describe '#to_label' do
22
+ it 'includes name, zone, and provider' do
23
+ subject.name = 'my-gce'
24
+ expected = 'my-gce (us-east1-b-Google)'
25
+ assert_equal expected, subject.to_label
26
+ end
27
+ end
28
+
29
+ describe '#capabilities' do
30
+ it 'returns image and new_volume' do
31
+ assert_equal %i[image new_volume], subject.capabilities
32
+ end
33
+ end
34
+
35
+ describe '#provided_attributes' do
36
+ it 'includes ip mapped to vm_ip_address' do
37
+ attrs = subject.provided_attributes
38
+ assert_equal :vm_ip_address, attrs[:ip]
39
+ end
40
+ end
41
+
42
+ describe '#user_data_supported?' do
43
+ it 'returns true' do
44
+ assert subject.user_data_supported?
45
+ end
46
+ end
47
+
48
+ describe '#zone' do
49
+ it 'reads from url attribute' do
50
+ subject.url = 'us-central1-a'
51
+ assert_equal 'us-central1-a', subject.zone
52
+ end
53
+ end
54
+
55
+ describe '#zone=' do
56
+ it 'writes to url attribute' do
57
+ subject.zone = 'europe-west1-b'
58
+ assert_equal 'europe-west1-b', subject.url
59
+ end
60
+ end
61
+
62
+ describe 'validations' do
63
+ it 'is valid with name, zone, and password' do
64
+ subject.name = 'test-gce'
65
+ assert subject.valid?, "Expected subject to be valid, got errors: #{subject.errors.full_messages}"
66
+ end
67
+
68
+ it 'requires password' do
69
+ cr = ForemanGoogle::GCE.new(zone: 'us-east1-b', password: nil)
70
+ assert_not cr.valid?
71
+ assert_includes cr.errors.attribute_names, :password
72
+ end
73
+
74
+ it 'requires zone' do
75
+ cr = ForemanGoogle::GCE.new(zone: nil, password: gauth_json)
76
+ assert_not cr.valid?
77
+ assert_includes cr.errors.attribute_names, :zone
78
+ end
79
+
80
+ it 'requires name' do
81
+ cr = ForemanGoogle::GCE.new(name: nil, zone: 'us-east1-b', password: gauth_json)
82
+ assert_not cr.valid?
83
+ assert_includes cr.errors.attribute_names, :name
84
+ end
85
+ end
86
+
87
+ describe '#zones' do
88
+ it 'returns zone names from client' do
89
+ zones = [OpenStruct.new(name: 'us-east1-b'), OpenStruct.new(name: 'us-west1-a')]
90
+ service.expects(:zones).returns(zones)
91
+ assert_equal %w[us-east1-b us-west1-a], subject.zones
92
+ end
93
+
94
+ it 'is aliased as available_zones' do
95
+ zones = [OpenStruct.new(name: 'us-east1-b')]
96
+ service.expects(:zones).returns(zones)
97
+ assert_equal %w[us-east1-b], subject.available_zones
98
+ end
99
+ end
100
+
101
+ describe '#networks' do
102
+ it 'returns network names from client' do
103
+ networks = [OpenStruct.new(name: 'default'), OpenStruct.new(name: 'custom')]
104
+ service.expects(:networks).returns(networks)
105
+ assert_equal %w[default custom], subject.networks
106
+ end
107
+ end
108
+
109
+ describe '#available_networks' do
110
+ it 'returns full network objects from client' do
111
+ network_objects = [OpenStruct.new(name: 'default')]
112
+ service.expects(:networks).returns(network_objects)
113
+ assert_equal network_objects, subject.available_networks
114
+ end
115
+ end
116
+
117
+ describe '#machine_types' do
118
+ it 'returns machine types for the zone from client' do
119
+ types = [OpenStruct.new(name: 'e2-micro')]
120
+ service.expects(:machine_types).with('us-east1-b').returns(types)
121
+ assert_equal types, subject.machine_types
122
+ end
123
+
124
+ it 'is aliased as available_flavors' do
125
+ types = [OpenStruct.new(name: 'n1-standard-1')]
126
+ service.expects(:machine_types).with('us-east1-b').returns(types)
127
+ assert_equal types, subject.available_flavors
128
+ end
129
+ end
130
+
131
+ describe '#available_images' do
132
+ it 'delegates to client.images with filter' do
133
+ images = [OpenStruct.new(name: 'centos-7')]
134
+ service.expects(:images).with(filter: nil).returns(images)
135
+ assert_equal images, subject.available_images
136
+ end
137
+
138
+ it 'passes custom filter to client' do
139
+ images = [OpenStruct.new(name: 'centos-7')]
140
+ service.expects(:images).with(filter: 'name = "centos"').returns(images)
141
+ assert_equal images, subject.available_images(filter: 'name = "centos"')
142
+ end
143
+ end
144
+
145
+ describe '#filter_for_images' do
146
+ it 'defaults to nil' do
147
+ assert_nil subject.filter_for_images
148
+ end
149
+ end
150
+
151
+ describe '#google_project_id' do
152
+ it 'delegates to client' do
153
+ assert_equal 'project_id', subject.google_project_id
154
+ end
155
+ end
156
+
21
157
  describe '#find_vm_by_uuid' do
22
158
  it 'does query gce' do
23
159
  instance = OpenStruct.new(**instance_args)
@@ -48,11 +184,29 @@ module ForemanGoogle
48
184
  end
49
185
 
50
186
  it 'iteration over the vms array' do
51
- subject.vms.each_with_index { |instance, i| assert instance.name, instances[i].name }
187
+ subject.vms.each_with_index { |instance, i| assert_equal instances[i].name, instance.name }
52
188
  end
53
189
 
54
190
  it 'all method' do
55
- subject.vms.all.each_with_index { |instance, i| assert instance.name, instances[i].name }
191
+ subject.vms.all.each_with_index { |instance, i| assert_equal instances[i].name, instance.name }
192
+ end
193
+ end
194
+
195
+ describe '#new_vm' do
196
+ it 'assigns provided attributes' do
197
+ vm = subject.new_vm(name: 'test-vm', machine_type: 'e2-micro')
198
+ assert_equal 'test-vm', vm.name
199
+ assert_equal 'e2-micro', vm.machine_type
200
+ end
201
+
202
+ it 'converts volumes_attributes nested hash' do
203
+ attrs = {
204
+ 'name' => 'test-vm',
205
+ 'volumes_attributes' => { '0' => { 'size_gb' => '20' } },
206
+ }
207
+ vm = subject.new_vm(attrs)
208
+ assert_equal 1, vm.volumes.size
209
+ assert_equal 20, vm.volumes.first.size_gb
56
210
  end
57
211
  end
58
212
 
@@ -65,5 +219,123 @@ module ForemanGoogle
65
219
  value { subject.create_vm({ image_id: 0 }) }.must_raise(::Foreman::Exception)
66
220
  end
67
221
  end
222
+
223
+ describe '#create_vm success path' do
224
+ let(:os_image) { OpenStruct.new(username: 'gce_user', name: 'centos-7') }
225
+ let(:gce_image) { OpenStruct.new(id: 42, self_link: 'image-link') }
226
+ let(:key_pair_obj) { OpenStruct.new(public: 'ssh-rsa AAAA') }
227
+ let(:vm) { mock('GoogleCompute') }
228
+
229
+ setup do
230
+ service.stubs(:image).returns(gce_image)
231
+ service.stubs(:instance)
232
+ subject.stubs(:images).returns(stub(find_by: os_image))
233
+ subject.stubs(:key_pair).returns(key_pair_obj)
234
+ subject.stubs(:new_vm).returns(vm)
235
+ vm.stubs(:hostname).returns('test-vm')
236
+
237
+ vm.expects(:create_volumes)
238
+ vm.expects(:create_instance)
239
+ vm.expects(:set_disk_auto_delete)
240
+ end
241
+
242
+ it 'creates volumes, instance, and sets auto-delete' do
243
+ result = subject.create_vm({ image_id: 42 })
244
+ assert_kind_of ForemanGoogle::GoogleCompute, result
245
+ end
246
+ end
247
+
248
+ describe '#create_vm rollback on error' do
249
+ let(:os_image) { OpenStruct.new(username: 'gce_user', name: 'centos-7') }
250
+ let(:gce_image) { OpenStruct.new(id: 42, self_link: 'image-link') }
251
+ let(:key_pair_obj) { OpenStruct.new(public: 'ssh-rsa AAAA') }
252
+ let(:vm) { mock('GoogleCompute') }
253
+
254
+ setup do
255
+ subject.stubs(:images).returns(stub(find_by: os_image))
256
+ service.stubs(:image).returns(gce_image)
257
+ subject.stubs(:key_pair).returns(key_pair_obj)
258
+ subject.stubs(:new_vm).returns(vm)
259
+ vm.stubs(:create_volumes).raises(::Google::Cloud::Error.new('quota exceeded'))
260
+
261
+ vm.expects(:destroy_volumes)
262
+ end
263
+
264
+ it 'destroys volumes and raises WrappedException' do
265
+ assert_raises(Foreman::WrappedException) { subject.create_vm({ image_id: 42 }) }
266
+ end
267
+ end
268
+
269
+ describe '#destroy_vm' do
270
+ it 'calls set_disk_auto_delete and delete_instance' do
271
+ service.expects(:set_disk_auto_delete).with('us-east1-b', 'vm-uuid')
272
+ service.expects(:delete_instance).with('us-east1-b', 'vm-uuid')
273
+ subject.destroy_vm('vm-uuid')
274
+ end
275
+
276
+ it 'returns true when VM not found' do
277
+ service.expects(:set_disk_auto_delete).raises(ActiveRecord::RecordNotFound)
278
+ assert subject.destroy_vm('missing-vm')
279
+ end
280
+ end
281
+
282
+ describe '#new_volume' do
283
+ it 'returns an AttachedDisk with 20 GB default' do
284
+ vol = subject.new_volume
285
+ assert_kind_of Google::Cloud::Compute::V1::AttachedDisk, vol
286
+ assert_equal 20, vol.disk_size_gb
287
+ end
288
+
289
+ it 'merges provided attributes' do
290
+ vol = subject.new_volume(device_name: 'boot-disk')
291
+ assert_equal 'boot-disk', vol.device_name
292
+ assert_equal 20, vol.disk_size_gb
293
+ end
294
+ end
295
+
296
+ describe '#console' do
297
+ let(:vm) { mock('GoogleCompute') }
298
+
299
+ setup do
300
+ subject.stubs(:find_vm_by_uuid).returns(vm)
301
+ end
302
+
303
+ it 'returns serial output when VM is ready' do
304
+ vm.stubs(:ready?).returns(true)
305
+ vm.stubs(:serial_port_output).returns('boot log...')
306
+ vm.stubs(:name).returns('test-vm')
307
+
308
+ result = subject.console('test-vm')
309
+ assert_equal 'boot log...', result['output']
310
+ assert_equal 'log', result[:type]
311
+ assert_equal 'test-vm', result[:name]
312
+ assert result['timestamp'].is_a?(Time)
313
+ end
314
+
315
+ it 'raises when VM is not ready' do
316
+ vm.stubs(:ready?).returns(false)
317
+ assert_raises(::Foreman::Exception) { subject.console('test-vm') }
318
+ end
319
+ end
320
+
321
+ describe '#associated_host' do
322
+ it 'calls associate_by with ip addresses' do
323
+ vm = OpenStruct.new(public_ip_address: '1.2.3.4', private_ip_address: '10.0.0.1')
324
+ subject.expects(:associate_by).with('ip', ['1.2.3.4', '10.0.0.1'])
325
+ subject.associated_host(vm)
326
+ end
327
+ end
328
+
329
+ describe '#vm_ready' do
330
+ it 'polls until vm is ready' do
331
+ vm = mock('GoogleCompute')
332
+ vm.stubs(:reload).returns(vm)
333
+ vm.stubs(:ready?).returns(false, true)
334
+ vm.stubs(:wait_for).yields.returns({ duration: 2 })
335
+
336
+ result = subject.vm_ready(vm)
337
+ assert_equal({ duration: 2 }, result)
338
+ end
339
+ end
68
340
  end
69
341
  end
@@ -194,20 +194,209 @@ module ForemanGoogle
194
194
 
195
195
  it '@network' do
196
196
  cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
197
- assert cr.network, 'default'
197
+ assert_equal 'default', cr.network
198
198
 
199
199
  cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { network: 'my-network' })
200
- assert cr.network, 'my-network'
200
+ assert_equal 'my-network', cr.network
201
201
  end
202
202
 
203
203
  it '@zone' do
204
204
  cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
205
- assert zone, cr.zone
205
+ assert_equal zone, cr.zone
206
206
  end
207
207
 
208
208
  it '@zone_name' do
209
209
  cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: instance)
210
- assert zone, cr.zone_name
210
+ assert_equal zone, cr.zone_name
211
+ end
212
+
213
+ describe '#status' do
214
+ it 'returns status when persisted' do
215
+ instance.status = 'RUNNING'
216
+ client.expects(:instance).with(zone, identity).returns(instance)
217
+ assert_equal 'RUNNING', subject.status
218
+ end
219
+
220
+ it 'returns false when not persisted' do
221
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
222
+ assert_not cr.status
223
+ end
224
+ end
225
+
226
+ describe '#state' do
227
+ it 'is an alias for status' do
228
+ instance.status = 'TERMINATED'
229
+ client.expects(:instance).with(zone, identity).returns(instance)
230
+ assert_equal subject.status, subject.state
231
+ end
232
+ end
233
+
234
+ describe '#start' do
235
+ it 'delegates to client.start when persisted' do
236
+ client.stubs(:instance).with(zone, identity).returns(instance)
237
+ client.expects(:start).with(zone, identity).returns(true)
238
+ subject.start
239
+ end
240
+
241
+ it 'raises when not persisted' do
242
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
243
+ assert_raises(Foreman::Exception) { cr.start }
244
+ end
245
+ end
246
+
247
+ describe '#stop' do
248
+ it 'delegates to client.stop when persisted' do
249
+ client.stubs(:instance).with(zone, identity).returns(instance)
250
+ client.expects(:stop).with(zone, identity).returns(true)
251
+ subject.stop
252
+ end
253
+
254
+ it 'raises when not persisted' do
255
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
256
+ assert_raises(Foreman::Exception) { cr.stop }
257
+ end
258
+ end
259
+
260
+ describe '#to_s' do
261
+ it 'returns the name' do
262
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { name: 'my-vm' })
263
+ assert_equal 'my-vm', cr.to_s
264
+ end
265
+ end
266
+
267
+ describe '#interfaces' do
268
+ it 'returns network_interfaces' do
269
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
270
+ assert_equal cr.network_interfaces, cr.interfaces
271
+ end
272
+ end
273
+
274
+ describe '#vm_description' do
275
+ it 'delegates to pretty_machine_type' do
276
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: instance)
277
+ assert_equal cr.pretty_machine_type, cr.vm_description
278
+ end
279
+ end
280
+
281
+ describe '#vm_ip_address - no network interfaces' do
282
+ it 'returns nil' do
283
+ empty_instance = OpenStruct.new(
284
+ name: 'instance', network_interfaces: [],
285
+ creation_timestamp: Time.zone.now, zone: zone,
286
+ machine_type: 'machineTypes/e2-micro'
287
+ )
288
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: empty_instance)
289
+ assert_nil cr.vm_ip_address
290
+ end
291
+ end
292
+
293
+ describe '#private_ip_address - no network interfaces' do
294
+ it 'returns nil' do
295
+ empty_instance = OpenStruct.new(
296
+ name: 'instance', network_interfaces: [],
297
+ creation_timestamp: Time.zone.now, zone: zone,
298
+ machine_type: 'machineTypes/e2-micro'
299
+ )
300
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: empty_instance)
301
+ assert_nil cr.private_ip_address
302
+ end
303
+ end
304
+
305
+ describe '#pretty_image_name - no disks' do
306
+ it 'returns nil' do
307
+ instance.disks = []
308
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: instance)
309
+ assert_nil cr.pretty_image_name
310
+ end
311
+ end
312
+
313
+ describe '#ip_addresses' do
314
+ it 'returns public and private IP' do
315
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: instance)
316
+ assert_equal ['1.2.3.4', '10.10.10.23'], cr.ip_addresses
317
+ end
318
+ end
319
+
320
+ describe '#serial_port_output' do
321
+ it 'delegates to client' do
322
+ client.stubs(:instance).with(zone, identity).returns(instance)
323
+ client.expects(:serial_port_output).with(zone, identity).returns(OpenStruct.new(contents: 'boot log'))
324
+ assert_equal 'boot log', subject.serial_port_output
325
+ end
326
+
327
+ it 'returns nil when client returns nil' do
328
+ client.stubs(:instance).with(zone, identity).returns(instance)
329
+ client.expects(:serial_port_output).with(zone, identity).returns(nil)
330
+ assert_nil subject.serial_port_output
331
+ end
332
+ end
333
+
334
+ describe '#volumes_attributes=' do
335
+ it 'is a no-op' do
336
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
337
+ cr.volumes_attributes = { '0' => { size_gb: 10 } }
338
+ assert cr.volumes.any?
339
+ end
340
+ end
341
+
342
+ describe '#reload - without identity' do
343
+ it 'returns nil' do
344
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
345
+ assert_nil cr.reload
346
+ end
347
+ end
348
+
349
+ describe '#create_volumes' do
350
+ it 'inserts disks and waits for READY' do
351
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { volumes: [{ size_gb: '20' }] })
352
+
353
+ client.expects(:insert_disk).with(zone, cr.volumes.first.insert_attrs)
354
+ client.expects(:disk).with(zone, cr.volumes.first.device_name).returns(OpenStruct.new(status: 'READY'))
355
+ client.expects(:wait_for).yields.returns({ duration: 1 })
356
+
357
+ cr.create_volumes
358
+ end
359
+ end
360
+
361
+ describe '#destroy_volumes' do
362
+ it 'deletes each volume disk' do
363
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { volumes: [{ size_gb: '20' }] })
364
+ client.expects(:delete_disk).with(zone, cr.volumes.first.device_name)
365
+ cr.destroy_volumes
366
+ end
367
+ end
368
+
369
+ describe '#create_instance' do
370
+ it 'calls insert_instance via ComputeAttributes' do
371
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { name: 'test-vm', machine_type: 'e2-micro' })
372
+ client.expects(:insert_instance).with(zone, has_key(:name))
373
+ cr.create_instance
374
+ end
375
+ end
376
+
377
+ describe '#set_disk_auto_delete' do
378
+ it 'delegates to client' do
379
+ args = { name: 'my-vm' }
380
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: args)
381
+ client.expects(:set_disk_auto_delete).with(zone, 'my-vm')
382
+ cr.set_disk_auto_delete
383
+ end
384
+ end
385
+
386
+ describe '#wait_for' do
387
+ it 'delegates to client.wait_for' do
388
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone)
389
+ client.expects(:wait_for).yields.returns({ duration: 0 })
390
+ result = cr.wait_for { true }
391
+ assert_equal({ duration: 0 }, result)
392
+ end
393
+ end
394
+
395
+ describe '#public_ip_address' do
396
+ it 'is an alias for vm_ip_address' do
397
+ cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, instance: instance)
398
+ assert_equal cr.vm_ip_address, cr.public_ip_address
399
+ end
211
400
  end
212
401
  end
213
402
  end
@@ -131,7 +131,7 @@ module ForemanGoogle
131
131
 
132
132
  result = subject.insert_instance('us-east1-b', args)
133
133
 
134
- assert 'insert', result.operation.operation_type
134
+ assert_equal 'insert', result.operation.operation_type
135
135
  assert_includes result.operation.target_link, 'foreman-test-google'
136
136
  end
137
137
 
@@ -156,14 +156,14 @@ module ForemanGoogle
156
156
  stub_request(:post, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/zones/us-east1-b/instances/instance_name/start')
157
157
  .to_return(status: 200, body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'instance_start.json')))
158
158
  result = subject.start('us-east1-b', 'instance_name')
159
- assert 'start', result.operation.operation_type
159
+ assert_equal 'start', result.operation.operation_type
160
160
  end
161
161
 
162
162
  it '#stop' do
163
163
  stub_request(:post, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/zones/us-east1-b/instances/instance_name/stop')
164
164
  .to_return(status: 200, body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'instance_stop.json')))
165
165
  result = subject.stop('us-east1-b', 'instance_name')
166
- assert 'stop', result.operation.operation_type
166
+ assert_equal 'stop', result.operation.operation_type
167
167
  end
168
168
 
169
169
  it '#set_disk_auto_delete' do
@@ -174,7 +174,7 @@ module ForemanGoogle
174
174
  .to_return(status: 200, body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'instance_set_disk_auto_delete.json')))
175
175
  result = subject.set_disk_auto_delete('us-east1-b', 'instance_name')
176
176
 
177
- assert 'device-1', result[0].source
177
+ assert_equal 'instance-1', result[0].device_name
178
178
  assert result[0].auto_delete
179
179
  end
180
180
  end
@@ -186,14 +186,14 @@ module ForemanGoogle
186
186
 
187
187
  result = subject.insert_disk('us-east1-b', { name: 'foreman-disk1', size_gb: 23 })
188
188
  assert_includes result.operation.target_link, 'foreman-disk1'
189
- assert 'insert', result.operation.operation_type
189
+ assert_equal 'insert', result.operation.operation_type
190
190
  end
191
191
 
192
192
  it '#get' do
193
193
  stub_request(:get, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/zones/us-east1-b/disks/foreman-disk1')
194
194
  .to_return(status: 200, body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'disks_get.json')))
195
195
  result = subject.disk('us-east1-b', 'foreman-disk1')
196
- assert 'foreman-disk1', result.name
196
+ assert_equal 'foreman-disk1', result.name
197
197
  end
198
198
 
199
199
  it '#delete' do
@@ -202,7 +202,59 @@ module ForemanGoogle
202
202
  result = subject.delete_disk('us-east1-b', 'foreman-disk1')
203
203
 
204
204
  assert_includes result.operation.target_link, 'foreman-disk1'
205
- assert 'delete', result.operation.operation_type
205
+ assert_equal 'delete', result.operation.operation_type
206
+ end
207
+ end
208
+
209
+ describe '#delete_instance' do
210
+ it 'deletes an instance' do
211
+ stub_request(:delete, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/zones/us-east1-b/instances/instance_name')
212
+ .to_return(status: 200, body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'instance_stop.json')))
213
+ result = subject.delete_instance('us-east1-b', 'instance_name')
214
+ assert_equal 'stop', result.operation.operation_type
215
+ end
216
+ end
217
+
218
+ describe '#image' do
219
+ setup do
220
+ stub_request(:get, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/global/images')
221
+ .to_return(body: File.read(File.join(__dir__, '..', '..', 'fixtures', 'images_coastal.json')))
222
+ end
223
+
224
+ it 'finds image by numeric id' do
225
+ subject.stub(:all_projects, []) do
226
+ result = subject.image(1)
227
+ assert_equal 'coastal-image', result.name
228
+ end
229
+ end
230
+
231
+ it 'returns nil when image not found' do
232
+ subject.stub(:all_projects, []) do
233
+ assert_nil subject.image(999)
234
+ end
235
+ end
236
+ end
237
+
238
+ describe '#serial_port_output' do
239
+ it 'returns serial port output' do
240
+ body = { contents: 'boot log output', next: 12_345, start: 0, selfLink: 'self-link' }.to_json
241
+ stub_request(:get, 'https://compute.googleapis.com/compute/v1/projects/coastal-haven-123456/zones/us-east1-b/instances/instance_name/serialPort')
242
+ .to_return(status: 200, body: body)
243
+ result = subject.serial_port_output('us-east1-b', 'instance_name')
244
+ assert_equal 'boot log output', result.contents
245
+ end
246
+ end
247
+
248
+ describe '#project_id' do
249
+ it 'returns the project_id from auth JSON' do
250
+ assert_equal 'coastal-haven-123456', subject.project_id
251
+ end
252
+ end
253
+
254
+ describe '#wait_for' do
255
+ it 'returns immediately when block yields true' do
256
+ result = subject.wait_for { true }
257
+ assert result[:duration] <= 1
206
258
  end
207
259
  end
208
260
  end