foreman_google 3.0.8 → 3.1.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.
- checksums.yaml +4 -4
- data/app/lib/google_cloud_compute/compute_attributes.rb +3 -1
- data/app/models/foreman_google/gce.rb +2 -4
- data/app/models/foreman_google/google_compute.rb +2 -2
- data/lib/foreman_google/engine.rb +1 -1
- data/lib/foreman_google/version.rb +1 -1
- data/test/controllers/foreman_google/api/v2/compute_resources_extensions_test.rb +71 -0
- data/test/models/concerns/foreman_google/host_managed_extensions_test.rb +46 -0
- data/test/models/foreman_google/gce_test.rb +275 -3
- data/test/models/foreman_google/google_compute_test.rb +193 -4
- data/test/unit/foreman_google/google_compute_adapter_test.rb +59 -7
- data/test/unit/google_cloud_compute/compute_attributes_test.rb +235 -0
- data/test/unit/google_cloud_compute/compute_collection_test.rb +78 -0
- data/test/unit/google_extensions/attached_disk_test.rb +35 -0
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6532064006813fbb57a5c62073d7f84f19fac3ae73899edf1e30b1f36d369f7b
|
|
4
|
+
data.tar.gz: 83449fb14ba738ad769c81f813effccf1bbceb79b7eeda2904ddca8c3fd6269f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 761b6101972b2d261daf7726c8ec8da2465909831a3bfa3d6de52ac1bae5a8a620ee8eef30728bebadd2a366111ce82e6f1989ae92a995e9cdf4045c61011bfd
|
|
7
|
+
data.tar.gz: 0244b37a972bae1d9a647f1949d11fb08cdf12bec291e8f69ff2d31ea6596319b355851b384331b9cce64d398cd3e6220ef85629f3e013ba33cd03fb9e13c4a9
|
|
@@ -29,12 +29,14 @@ module GoogleCloudCompute
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def for_instance(instance)
|
|
32
|
+
first_nic = instance.network_interfaces[0]
|
|
33
|
+
|
|
32
34
|
{
|
|
33
35
|
name: instance.name, hostname: instance.name,
|
|
34
36
|
creation_timestamp: instance.creation_timestamp.to_datetime,
|
|
35
37
|
zone_name: instance.zone.split('/').last,
|
|
36
38
|
machine_type: instance.machine_type,
|
|
37
|
-
network:
|
|
39
|
+
network: first_nic&.network&.split('/')&.last,
|
|
38
40
|
network_interfaces: instance.network_interfaces,
|
|
39
41
|
volumes: instance.disks, metadata: instance.metadata
|
|
40
42
|
}
|
|
@@ -96,10 +96,8 @@ module ForemanGoogle
|
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def setup_key_pair
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
key = ::SSHKey.generate
|
|
102
|
-
build_key_pair name: "foreman-#{id}#{Foreman.uuid}", secret: key.private_key, public: key.ssh_public_key
|
|
99
|
+
key_pair = Foreman::Provision::SshKey.generate
|
|
100
|
+
build_key_pair name: "foreman-#{id}#{Foreman.uuid}", secret: key_pair.private_key, public: key_pair.public_key
|
|
103
101
|
end
|
|
104
102
|
|
|
105
103
|
def self.provider_friendly_name
|
|
@@ -35,12 +35,12 @@ module ForemanGoogle
|
|
|
35
35
|
alias_method :state, :status
|
|
36
36
|
|
|
37
37
|
def start
|
|
38
|
-
raise Foreman::Exception('unable to start machine that is not persisted') unless persisted?
|
|
38
|
+
raise Foreman::Exception, N_('unable to start machine that is not persisted') unless persisted?
|
|
39
39
|
@client.start(@zone, identity)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def stop
|
|
43
|
-
raise Foreman::Exception('unable to stop machine that is not persisted') unless persisted?
|
|
43
|
+
raise Foreman::Exception, N_('unable to stop machine that is not persisted') unless persisted?
|
|
44
44
|
@client.stop(@zone, identity)
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -18,7 +18,7 @@ module ForemanGoogle
|
|
|
18
18
|
|
|
19
19
|
initializer 'foreman_google.register_plugin', before: :finisher_hook do |_app|
|
|
20
20
|
Foreman::Plugin.register :foreman_google do
|
|
21
|
-
requires_foreman '>=
|
|
21
|
+
requires_foreman '>= 5.1'
|
|
22
22
|
register_global_js_file 'global'
|
|
23
23
|
register_gettext
|
|
24
24
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'test_google_helper'
|
|
2
|
+
|
|
3
|
+
module ForemanGoogle
|
|
4
|
+
module Api
|
|
5
|
+
module V2
|
|
6
|
+
class ComputeResourcesExtensionsTest < GoogleTestCase
|
|
7
|
+
describe 'the real API controller integration' do
|
|
8
|
+
it 'loads the Google parameter extension into the compute resource controller' do
|
|
9
|
+
assert_includes ::Api::V2::ComputeResourcesController.ancestors,
|
|
10
|
+
::Foreman::Controller::Parameters::ComputeResourceExtension
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'permits Google compute resource parameters' do
|
|
14
|
+
context = ::Api::V2::ComputeResourcesController.parameter_filter_context
|
|
15
|
+
params = ActionController::Parameters.new(
|
|
16
|
+
compute_resource: { key_path: '/tmp/key.json', zone: 'us-east1-b' }
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
filtered = ::Api::V2::ComputeResourcesController
|
|
20
|
+
.compute_resource_params_filter
|
|
21
|
+
.filter_params(params, context)
|
|
22
|
+
|
|
23
|
+
assert_equal({ 'key_path' => '/tmp/key.json', 'zone' => 'us-east1-b' }, filtered.to_h)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
let(:controller_class) do
|
|
28
|
+
Class.new do
|
|
29
|
+
def self.before_action(*)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
include ForemanGoogle::Api::V2::ComputeResourcesExtensions
|
|
33
|
+
|
|
34
|
+
attr_accessor :params
|
|
35
|
+
|
|
36
|
+
def compute_resource_params
|
|
37
|
+
params[:compute_resource]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe '#read_key' do
|
|
43
|
+
it 'reads key file content into password for GCE provider' do
|
|
44
|
+
key_content = '{"type": "service_account", "project_id": "test"}'
|
|
45
|
+
|
|
46
|
+
Tempfile.open('gce_key') do |f|
|
|
47
|
+
f.write(key_content)
|
|
48
|
+
f.flush
|
|
49
|
+
|
|
50
|
+
ctrl = controller_class.new
|
|
51
|
+
ctrl.params = { 'compute_resource' => { 'provider' => 'GCE', 'key_path' => f.path } }.with_indifferent_access
|
|
52
|
+
ctrl.send(:read_key)
|
|
53
|
+
|
|
54
|
+
assert_equal key_content, ctrl.params[:compute_resource][:password]
|
|
55
|
+
assert_not ctrl.params[:compute_resource].key?('key_path')
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it 'skips when provider is not GCE' do
|
|
60
|
+
ctrl = controller_class.new
|
|
61
|
+
ctrl.params = { 'compute_resource' => { 'provider' => 'EC2', 'key_path' => '/nonexistent' } }.with_indifferent_access
|
|
62
|
+
ctrl.send(:read_key)
|
|
63
|
+
|
|
64
|
+
assert_nil ctrl.params[:compute_resource][:password]
|
|
65
|
+
assert_equal '/nonexistent', ctrl.params[:compute_resource]['key_path']
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'test_google_helper'
|
|
2
|
+
|
|
3
|
+
module ForemanGoogle
|
|
4
|
+
class HostManagedExtensionsTest < GoogleTestCase
|
|
5
|
+
let(:host_class) do
|
|
6
|
+
Class.new do
|
|
7
|
+
include ForemanGoogle::HostManagedExtensions
|
|
8
|
+
attr_accessor :vm
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#ip_addresses' do
|
|
13
|
+
it 'returns vm ip_addresses when vm exists' do
|
|
14
|
+
vm = mock('GoogleCompute')
|
|
15
|
+
vm.expects(:ip_addresses).returns(['1.2.3.4', '10.0.0.1'])
|
|
16
|
+
|
|
17
|
+
host = host_class.new
|
|
18
|
+
host.vm = vm
|
|
19
|
+
assert_equal ['1.2.3.4', '10.0.0.1'], host.ip_addresses
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'returns empty array when vm is nil' do
|
|
23
|
+
host = host_class.new
|
|
24
|
+
host.vm = nil
|
|
25
|
+
assert_empty host.ip_addresses
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe '#vm_ip_address' do
|
|
30
|
+
it 'returns vm public IP when vm exists' do
|
|
31
|
+
vm = mock('GoogleCompute')
|
|
32
|
+
vm.expects(:vm_ip_address).returns('1.2.3.4')
|
|
33
|
+
|
|
34
|
+
host = host_class.new
|
|
35
|
+
host.vm = vm
|
|
36
|
+
assert_equal '1.2.3.4', host.vm_ip_address
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'returns nil when vm is nil' do
|
|
40
|
+
host = host_class.new
|
|
41
|
+
host.vm = nil
|
|
42
|
+
assert_nil host.vm_ip_address
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -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: '
|
|
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|
|
|
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|
|
|
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
|
-
|
|
197
|
+
assert_equal 'default', cr.network
|
|
198
198
|
|
|
199
199
|
cr = ForemanGoogle::GoogleCompute.new(client: client, zone: zone, args: { network: 'my-network' })
|
|
200
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
require 'test_google_helper'
|
|
2
|
+
|
|
3
|
+
module GoogleCloudCompute
|
|
4
|
+
class ComputeAttributesTest < GoogleTestCase
|
|
5
|
+
let(:client) { mock('GoogleAdapter') }
|
|
6
|
+
subject { GoogleCloudCompute::ComputeAttributes.new(client) }
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
client.stubs(:project_id).returns('my-project')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe '#for_new' do
|
|
13
|
+
describe 'name handling' do
|
|
14
|
+
it 'generates a default name with foreman- prefix when name is nil' do
|
|
15
|
+
result = subject.for_new({})
|
|
16
|
+
assert_match(/\Aforeman-\d+\z/, result[:name])
|
|
17
|
+
assert_equal result[:name], result[:hostname]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'parameterizes the provided name' do
|
|
21
|
+
result = subject.for_new(name: 'My VM Name')
|
|
22
|
+
assert_equal 'my-vm-name', result[:name]
|
|
23
|
+
assert_equal 'my-vm-name', result[:hostname]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe 'network handling' do
|
|
28
|
+
it 'defaults to default network' do
|
|
29
|
+
result = subject.for_new({})
|
|
30
|
+
assert_equal 'default', result[:network]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'uses custom network name' do
|
|
34
|
+
result = subject.for_new(network: 'custom-net')
|
|
35
|
+
assert_equal 'custom-net', result[:network]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'builds full URL without external IP' do
|
|
39
|
+
result = subject.for_new(network: 'default')
|
|
40
|
+
expected_url = 'https://compute.googleapis.com/compute/v1/projects/my-project/global/networks/default'
|
|
41
|
+
assert_equal expected_url, result[:network_interfaces][0][:network]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'adds NAT access config with external IP' do
|
|
45
|
+
result = subject.for_new(associate_external_ip: '1')
|
|
46
|
+
nic = result[:network_interfaces][0]
|
|
47
|
+
assert_equal 'global/networks/default', nic[:network]
|
|
48
|
+
assert_equal [{ name: 'External NAT', type: 'ONE_TO_ONE_NAT' }], nic[:access_configs]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'uses custom network interfaces with external IP' do
|
|
52
|
+
result = subject.for_new(
|
|
53
|
+
associate_external_ip: '1',
|
|
54
|
+
network_interfaces: [{ network: 'global/networks/my-net' }]
|
|
55
|
+
)
|
|
56
|
+
nic = result[:network_interfaces][0]
|
|
57
|
+
assert_equal 'global/networks/my-net', nic[:network]
|
|
58
|
+
assert_equal [{ name: 'External NAT', type: 'ONE_TO_ONE_NAT' }], nic[:access_configs]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe 'associate_external_ip boolean casting' do
|
|
63
|
+
it 'casts string 1 to true' do
|
|
64
|
+
result = subject.for_new(associate_external_ip: '1')
|
|
65
|
+
assert result[:associate_external_ip]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'casts string 0 to false' do
|
|
69
|
+
result = subject.for_new(associate_external_ip: '0')
|
|
70
|
+
assert_not result[:associate_external_ip]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'defaults to false when absent' do
|
|
74
|
+
result = subject.for_new({})
|
|
75
|
+
assert_not result[:associate_external_ip]
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
describe 'volume construction' do
|
|
80
|
+
it 'creates a default 20 GB disk when no volumes' do
|
|
81
|
+
result = subject.for_new({})
|
|
82
|
+
assert_equal 1, result[:volumes].length
|
|
83
|
+
assert_equal 20, result[:volumes].first.disk_size_gb
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it 'creates volumes with custom size' do
|
|
87
|
+
result = subject.for_new(volumes: [{ size_gb: '50' }])
|
|
88
|
+
assert_equal 50, result[:volumes].first.disk_size_gb
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it 'sets source image on first volume when image_id is provided' do
|
|
92
|
+
image = OpenStruct.new(id: 1, self_link: 'projects/my-project/global/images/centos-7')
|
|
93
|
+
client.stubs(:image).with(1).returns(image)
|
|
94
|
+
|
|
95
|
+
result = subject.for_new(
|
|
96
|
+
volumes: [{ size_gb: '20' }, { size_gb: '30' }],
|
|
97
|
+
image_id: '1'
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
assert_equal 'projects/my-project/global/images/centos-7', result[:volumes][0].source
|
|
101
|
+
assert_empty result[:volumes][1].source
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it 'names disks sequentially' do
|
|
105
|
+
result = subject.for_new(name: 'test-vm', volumes: [{ size_gb: '20' }, { size_gb: '30' }])
|
|
106
|
+
assert_equal 'test-vm-disk1', result[:volumes][0].device_name
|
|
107
|
+
assert_equal 'test-vm-disk2', result[:volumes][1].device_name
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it 'accepts disk_size_gb as an alternative key' do
|
|
111
|
+
result = subject.for_new(volumes: [{ disk_size_gb: '40' }])
|
|
112
|
+
assert_equal 40, result[:volumes].first.disk_size_gb
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe 'metadata construction' do
|
|
117
|
+
it 'includes ssh-keys' do
|
|
118
|
+
result = subject.for_new(username: 'gce_user', public_key: 'ssh-rsa AAAA')
|
|
119
|
+
ssh_item = result[:metadata][:items].find { |i| i[:key] == 'ssh-keys' }
|
|
120
|
+
assert_equal 'gce_user:ssh-rsa AAAA', ssh_item[:value]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'includes user-data when present' do
|
|
124
|
+
result = subject.for_new(username: 'user', public_key: 'key', user_data: '#cloud-config')
|
|
125
|
+
ud_item = result[:metadata][:items].find { |i| i[:key] == 'user-data' }
|
|
126
|
+
assert_equal '#cloud-config', ud_item[:value]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'omits user-data when blank' do
|
|
130
|
+
result = subject.for_new(username: 'user', public_key: 'key')
|
|
131
|
+
keys = result[:metadata][:items].map { |i| i[:key] }
|
|
132
|
+
assert_not_includes keys, 'user-data'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
it 'passes through machine_type and image_id' do
|
|
137
|
+
result = subject.for_new(machine_type: 'e2-micro', image_id: '42')
|
|
138
|
+
assert_equal 'e2-micro', result[:machine_type]
|
|
139
|
+
assert_equal '42', result[:image_id]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe '#for_create' do
|
|
144
|
+
let(:instance) do
|
|
145
|
+
vol1 = Google::Cloud::Compute::V1::AttachedDisk.new(device_name: 'vm-disk1')
|
|
146
|
+
vol2 = Google::Cloud::Compute::V1::AttachedDisk.new(device_name: 'vm-disk2')
|
|
147
|
+
OpenStruct.new(
|
|
148
|
+
name: 'test-vm',
|
|
149
|
+
zone: 'us-east1-b',
|
|
150
|
+
machine_type: 'e2-micro',
|
|
151
|
+
volumes: [vol1, vol2],
|
|
152
|
+
network_interfaces: [{ network: 'global/networks/default' }],
|
|
153
|
+
metadata: { items: [{ key: 'ssh-keys', value: 'user:key' }] }
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'builds machine_type URL from zone' do
|
|
158
|
+
result = subject.for_create(instance)
|
|
159
|
+
assert_equal 'zones/us-east1-b/machineTypes/e2-micro', result[:machine_type]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
it 'builds disk source paths' do
|
|
163
|
+
result = subject.for_create(instance)
|
|
164
|
+
assert_equal 'zones/us-east1-b/disks/vm-disk1', result[:disks][0][:source]
|
|
165
|
+
assert_equal 'zones/us-east1-b/disks/vm-disk2', result[:disks][1][:source]
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
it 'marks first disk as boot' do
|
|
169
|
+
result = subject.for_create(instance)
|
|
170
|
+
assert result[:disks][0][:boot]
|
|
171
|
+
assert_not result[:disks][1][:boot]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
it 'includes network_interfaces and metadata' do
|
|
175
|
+
result = subject.for_create(instance)
|
|
176
|
+
assert_equal instance.network_interfaces, result[:network_interfaces]
|
|
177
|
+
assert_equal instance.metadata, result[:metadata]
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it 'includes name' do
|
|
181
|
+
result = subject.for_create(instance)
|
|
182
|
+
assert_equal 'test-vm', result[:name]
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
describe '#for_instance' do
|
|
187
|
+
let(:nics) do
|
|
188
|
+
[OpenStruct.new(network: 'projects/my-project/global/networks/custom', network_i_p: '10.0.0.1')]
|
|
189
|
+
end
|
|
190
|
+
let(:instance) do
|
|
191
|
+
OpenStruct.new(
|
|
192
|
+
name: 'existing-vm',
|
|
193
|
+
creation_timestamp: Time.zone.parse('2024-01-15 10:00:00'),
|
|
194
|
+
zone: 'projects/my-project/zones/us-west1-a',
|
|
195
|
+
machine_type: 'zones/us-west1-a/machineTypes/n1-standard-1',
|
|
196
|
+
network_interfaces: nics,
|
|
197
|
+
disks: [OpenStruct.new(device_name: 'disk1')],
|
|
198
|
+
metadata: { items: [] }
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'extracts zone_name from full zone URL' do
|
|
203
|
+
result = subject.for_instance(instance)
|
|
204
|
+
assert_equal 'us-west1-a', result[:zone_name]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
it 'extracts network name from full URL' do
|
|
208
|
+
result = subject.for_instance(instance)
|
|
209
|
+
assert_equal 'custom', result[:network]
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it 'converts creation_timestamp to DateTime' do
|
|
213
|
+
result = subject.for_instance(instance)
|
|
214
|
+
assert_kind_of DateTime, result[:creation_timestamp]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
it 'passes through name and hostname' do
|
|
218
|
+
result = subject.for_instance(instance)
|
|
219
|
+
assert_equal 'existing-vm', result[:name]
|
|
220
|
+
assert_equal 'existing-vm', result[:hostname]
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'passes through disks as volumes and metadata' do
|
|
224
|
+
result = subject.for_instance(instance)
|
|
225
|
+
assert_equal instance.disks, result[:volumes]
|
|
226
|
+
assert_equal instance.metadata, result[:metadata]
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'passes through machine_type' do
|
|
230
|
+
result = subject.for_instance(instance)
|
|
231
|
+
assert_equal instance.machine_type, result[:machine_type]
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require 'test_google_helper'
|
|
2
|
+
|
|
3
|
+
module GoogleCloudCompute
|
|
4
|
+
class ComputeCollectionTest < GoogleTestCase
|
|
5
|
+
let(:client) { mock('GoogleAdapter') }
|
|
6
|
+
let(:zone) { 'us-east1-b' }
|
|
7
|
+
|
|
8
|
+
setup do
|
|
9
|
+
client.stubs(:project_id).returns('project_id')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe 'with instances' do
|
|
13
|
+
let(:instances) do
|
|
14
|
+
nics = [OpenStruct.new(access_configs: [OpenStruct.new(nat_i_p: '1.2.3.4')],
|
|
15
|
+
network: 'projects/project_id/global/networks/default', network_i_p: '10.0.0.1')]
|
|
16
|
+
[
|
|
17
|
+
OpenStruct.new(id: 1, name: 'vm-1', network_interfaces: nics,
|
|
18
|
+
creation_timestamp: Time.zone.now, zone: zone, machine_type: 'machineTypes/e2-micro'),
|
|
19
|
+
OpenStruct.new(id: 2, name: 'vm-2', network_interfaces: nics,
|
|
20
|
+
creation_timestamp: Time.zone.now, zone: zone, machine_type: 'machineTypes/e2-small'),
|
|
21
|
+
]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
setup do
|
|
25
|
+
client.stubs(:instances).with(zone).returns(instances)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'wraps instances as GoogleCompute objects' do
|
|
29
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
30
|
+
collection.each do |vm|
|
|
31
|
+
assert_kind_of ForemanGoogle::GoogleCompute, vm
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it 'iterates via each' do
|
|
36
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
37
|
+
names = collection.map(&:name)
|
|
38
|
+
assert_equal %w[vm-1 vm-2], names
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'returns all VMs via all' do
|
|
42
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
43
|
+
assert_equal 2, collection.all.length
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'is Enumerable' do
|
|
47
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
48
|
+
assert_kind_of Enumerable, collection
|
|
49
|
+
assert_equal 2, collection.count
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe 'with empty instance list' do
|
|
54
|
+
setup do
|
|
55
|
+
client.stubs(:instances).with(zone).returns([])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'returns empty collection' do
|
|
59
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
60
|
+
assert_empty collection.all
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'iterates zero times' do
|
|
64
|
+
collection = GoogleCloudCompute::ComputeCollection.new(client, zone)
|
|
65
|
+
count = 0
|
|
66
|
+
collection.each { count += 1 }
|
|
67
|
+
assert_equal 0, count
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
describe 'with attrs' do
|
|
72
|
+
it 'passes attrs to client.instances' do
|
|
73
|
+
client.expects(:instances).with(zone, filter: 'name = "test"').returns([])
|
|
74
|
+
GoogleCloudCompute::ComputeCollection.new(client, zone, filter: 'name = "test"')
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -12,6 +12,41 @@ module GoogleExtensions
|
|
|
12
12
|
disk = Google::Cloud::Compute::V1::AttachedDisk.new
|
|
13
13
|
assert_empty disk.insert_attrs[:source_image]
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
it 'includes device_name as name' do
|
|
17
|
+
disk = Google::Cloud::Compute::V1::AttachedDisk.new(device_name: 'boot-disk', disk_size_gb: 30)
|
|
18
|
+
attrs = disk.insert_attrs
|
|
19
|
+
assert_equal 'boot-disk', attrs[:name]
|
|
20
|
+
assert_equal 30, attrs[:size_gb]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
describe '#size_gb' do
|
|
25
|
+
it 'returns disk_size_gb' do
|
|
26
|
+
disk = Google::Cloud::Compute::V1::AttachedDisk.new(disk_size_gb: 50)
|
|
27
|
+
assert_equal 50, disk.size_gb
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe '#persisted?' do
|
|
32
|
+
it 'returns nil' do
|
|
33
|
+
disk = Google::Cloud::Compute::V1::AttachedDisk.new
|
|
34
|
+
assert_nil disk.persisted?
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#id' do
|
|
39
|
+
it 'returns nil' do
|
|
40
|
+
disk = Google::Cloud::Compute::V1::AttachedDisk.new
|
|
41
|
+
assert_nil disk.id
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe '#_delete' do
|
|
46
|
+
it 'returns nil' do
|
|
47
|
+
disk = Google::Cloud::Compute::V1::AttachedDisk.new
|
|
48
|
+
assert_nil disk._delete
|
|
49
|
+
end
|
|
15
50
|
end
|
|
16
51
|
end
|
|
17
52
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_google
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0
|
|
4
|
+
version: 3.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- The Foreman Team
|
|
@@ -141,6 +141,7 @@ files:
|
|
|
141
141
|
- locale/zh_TW/LC_MESSAGES/foreman_google.mo
|
|
142
142
|
- locale/zh_TW/foreman_google.po
|
|
143
143
|
- package.json
|
|
144
|
+
- test/controllers/foreman_google/api/v2/compute_resources_extensions_test.rb
|
|
144
145
|
- test/factories/gce.rb
|
|
145
146
|
- test/fixtures/disks_delete.json
|
|
146
147
|
- test/fixtures/disks_get.json
|
|
@@ -160,10 +161,13 @@ files:
|
|
|
160
161
|
- test/fixtures/operation_error.json
|
|
161
162
|
- test/fixtures/operation_get.json
|
|
162
163
|
- test/fixtures/zones.json
|
|
164
|
+
- test/models/concerns/foreman_google/host_managed_extensions_test.rb
|
|
163
165
|
- test/models/foreman_google/gce_test.rb
|
|
164
166
|
- test/models/foreman_google/google_compute_test.rb
|
|
165
167
|
- test/test_google_helper.rb
|
|
166
168
|
- test/unit/foreman_google/google_compute_adapter_test.rb
|
|
169
|
+
- test/unit/google_cloud_compute/compute_attributes_test.rb
|
|
170
|
+
- test/unit/google_cloud_compute/compute_collection_test.rb
|
|
167
171
|
- test/unit/google_extensions/attached_disk_test.rb
|
|
168
172
|
- webpack/global_index.js
|
|
169
173
|
- webpack/global_test_setup.js
|
|
@@ -196,10 +200,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
196
200
|
- !ruby/object:Gem::Version
|
|
197
201
|
version: '0'
|
|
198
202
|
requirements: []
|
|
199
|
-
rubygems_version: 4.0.
|
|
203
|
+
rubygems_version: 4.0.20
|
|
200
204
|
specification_version: 4
|
|
201
205
|
summary: Google Compute Engine plugin for the Foreman
|
|
202
206
|
test_files:
|
|
207
|
+
- test/controllers/foreman_google/api/v2/compute_resources_extensions_test.rb
|
|
203
208
|
- test/factories/gce.rb
|
|
204
209
|
- test/fixtures/disks_delete.json
|
|
205
210
|
- test/fixtures/disks_get.json
|
|
@@ -219,8 +224,11 @@ test_files:
|
|
|
219
224
|
- test/fixtures/operation_error.json
|
|
220
225
|
- test/fixtures/operation_get.json
|
|
221
226
|
- test/fixtures/zones.json
|
|
227
|
+
- test/models/concerns/foreman_google/host_managed_extensions_test.rb
|
|
222
228
|
- test/models/foreman_google/gce_test.rb
|
|
223
229
|
- test/models/foreman_google/google_compute_test.rb
|
|
224
230
|
- test/test_google_helper.rb
|
|
225
231
|
- test/unit/foreman_google/google_compute_adapter_test.rb
|
|
232
|
+
- test/unit/google_cloud_compute/compute_attributes_test.rb
|
|
233
|
+
- test/unit/google_cloud_compute/compute_collection_test.rb
|
|
226
234
|
- test/unit/google_extensions/attached_disk_test.rb
|