knife-google 3.3.6 → 3.3.7
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/lib/knife-google/version.rb +1 -1
- metadata +2 -33
- data/.expeditor/config.yml +0 -41
- data/.expeditor/update_version.sh +0 -12
- data/.github/CODEOWNERS +0 -4
- data/.github/ISSUE_TEMPLATE.md +0 -21
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -14
- data/.gitignore +0 -26
- data/.rspec +0 -3
- data/.travis.yml +0 -27
- data/CHANGELOG.md +0 -252
- data/Gemfile +0 -30
- data/README.md +0 -305
- data/RELEASE_NOTES.md +0 -18
- data/Rakefile +0 -35
- data/TESTING.md +0 -48
- data/VERSION +0 -1
- data/knife-google.gemspec +0 -25
- data/spec/cloud/google_service_helpers_spec.rb +0 -119
- data/spec/cloud/google_service_spec.rb +0 -1021
- data/spec/google_disk_create_spec.rb +0 -86
- data/spec/google_disk_delete_spec.rb +0 -78
- data/spec/google_disk_list_spec.rb +0 -107
- data/spec/google_project_quotas_spec.rb +0 -77
- data/spec/google_region_list_spec.rb +0 -79
- data/spec/google_region_quotas_spec.rb +0 -122
- data/spec/google_server_create_spec.rb +0 -243
- data/spec/google_server_delete_spec.rb +0 -53
- data/spec/google_server_list_spec.rb +0 -91
- data/spec/google_server_show_spec.rb +0 -74
- data/spec/google_zone_list_spec.rb +0 -73
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
4
|
-
# Copyright:: Copyright (c) 2016 Chef Software, Inc.
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
#
|
19
|
-
|
20
|
-
require "chef/knife/cloud/google_service"
|
21
|
-
require "chef/knife/cloud/google_service_helpers"
|
22
|
-
|
23
|
-
class Tester
|
24
|
-
include Chef::Knife::Cloud::GoogleServiceHelpers
|
25
|
-
end
|
26
|
-
|
27
|
-
describe Chef::Knife::Cloud::GoogleServiceHelpers do
|
28
|
-
let(:tester) { Tester.new }
|
29
|
-
|
30
|
-
describe "#create_service_instance" do
|
31
|
-
it "creates a GoogleService instance" do
|
32
|
-
expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("test_project")
|
33
|
-
expect(tester).to receive(:locate_config_value).with(:gce_zone).and_return("test_zone")
|
34
|
-
expect(tester).to receive(:locate_config_value).with(:request_timeout).and_return(123)
|
35
|
-
expect(tester).to receive(:locate_config_value).with(:request_refresh_rate).and_return(321)
|
36
|
-
expect(tester).to receive(:locate_config_value).with(:max_pages).and_return(456)
|
37
|
-
expect(tester).to receive(:locate_config_value).with(:max_page_size).and_return(654)
|
38
|
-
|
39
|
-
expect(Chef::Knife::Cloud::GoogleService).to receive(:new).with(
|
40
|
-
project: "test_project",
|
41
|
-
zone: "test_zone",
|
42
|
-
wait_time: 123,
|
43
|
-
refresh_rate: 321,
|
44
|
-
max_pages: 456,
|
45
|
-
max_page_size: 654
|
46
|
-
).and_return("service_object")
|
47
|
-
|
48
|
-
expect(tester.create_service_instance).to eq("service_object")
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe "#check_for_missing_config_values" do
|
53
|
-
it "does not raise an exception if all parameters are present" do
|
54
|
-
expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("project")
|
55
|
-
expect(tester).to receive(:locate_config_value).with(:key1).and_return("value1")
|
56
|
-
expect(tester).to receive(:locate_config_value).with(:key2).and_return("value2")
|
57
|
-
|
58
|
-
expect { tester.check_for_missing_config_values!(:key1, :key2) }.not_to raise_error
|
59
|
-
end
|
60
|
-
|
61
|
-
it "raises an exception if a parameter is missing" do
|
62
|
-
ui = double("ui")
|
63
|
-
expect(tester).to receive(:ui).and_return(ui)
|
64
|
-
expect(tester).to receive(:locate_config_value).with(:gce_project).and_return("project")
|
65
|
-
expect(tester).to receive(:locate_config_value).with(:key1).and_return("value1")
|
66
|
-
expect(tester).to receive(:locate_config_value).with(:key2).and_return(nil)
|
67
|
-
expect(ui).to receive(:error).with("The following required parameters are missing: key2")
|
68
|
-
expect { tester.check_for_missing_config_values!(:key1, :key2) }.to raise_error(RuntimeError)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "#private_ip_for" do
|
73
|
-
it "returns the IP address if it exists" do
|
74
|
-
network_interface = double("network_interface", network_ip: "1.2.3.4")
|
75
|
-
server = double("server", network_interfaces: [network_interface])
|
76
|
-
|
77
|
-
expect(tester.private_ip_for(server)).to eq("1.2.3.4")
|
78
|
-
end
|
79
|
-
|
80
|
-
it "returns 'unknown' if the IP cannot be found" do
|
81
|
-
server = double("server")
|
82
|
-
|
83
|
-
expect(server).to receive(:network_interfaces).and_raise(NoMethodError)
|
84
|
-
expect(tester.private_ip_for(server)).to eq("unknown")
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
describe "#public_ip_for" do
|
89
|
-
it "returns the IP address if it exists" do
|
90
|
-
access_config = double("access_config", nat_ip: "4.3.2.1")
|
91
|
-
network_interface = double("network_interface", access_configs: [access_config])
|
92
|
-
server = double("server", network_interfaces: [network_interface])
|
93
|
-
|
94
|
-
expect(tester.public_ip_for(server)).to eq("4.3.2.1")
|
95
|
-
end
|
96
|
-
|
97
|
-
it "returns 'unknown' if the IP cannot be found" do
|
98
|
-
network_interface = double("network_interface")
|
99
|
-
server = double("server", network_interfaces: [network_interface])
|
100
|
-
|
101
|
-
expect(network_interface).to receive(:access_configs).and_raise(NoMethodError)
|
102
|
-
expect(tester.public_ip_for(server)).to eq("unknown")
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
describe "#valid_disk_size?" do
|
107
|
-
it "returns true if the disk is between 10 and 10,000" do
|
108
|
-
expect(tester.valid_disk_size?(50)).to eq(true)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "returns false if the disk is less than 10" do
|
112
|
-
expect(tester.valid_disk_size?(5)).to eq(false)
|
113
|
-
end
|
114
|
-
|
115
|
-
it "returns false if the disk is greater than 10,000" do
|
116
|
-
expect(tester.valid_disk_size?(20_000)).to eq(false)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
@@ -1,1021 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
4
|
-
# Copyright:: Copyright (c) 2016 Chef Software, Inc.
|
5
|
-
# License:: Apache License, Version 2.0
|
6
|
-
#
|
7
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
-
# you may not use this file except in compliance with the License.
|
9
|
-
# You may obtain a copy of the License at
|
10
|
-
#
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
-
#
|
13
|
-
# Unless required by applicable law or agreed to in writing, software
|
14
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
-
# See the License for the specific language governing permissions and
|
17
|
-
# limitations under the License.
|
18
|
-
#
|
19
|
-
|
20
|
-
require "chef/knife"
|
21
|
-
require "chef/knife/cloud/exceptions"
|
22
|
-
require "chef/knife/cloud/google_service"
|
23
|
-
require "support/shared_examples_for_service"
|
24
|
-
|
25
|
-
shared_examples_for "a paginated list fetcher" do |fetch_method, items_method, *args|
|
26
|
-
it "retrieves paginated results from the API" do
|
27
|
-
expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args)
|
28
|
-
subject
|
29
|
-
end
|
30
|
-
|
31
|
-
it "returns the results if they exist" do
|
32
|
-
expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args).and_return("results")
|
33
|
-
expect(subject).to eq("results")
|
34
|
-
end
|
35
|
-
|
36
|
-
it "returns an empty array if there are no results" do
|
37
|
-
expect(service).to receive(:paginated_results).with(fetch_method, items_method, *args).and_return(nil)
|
38
|
-
expect(subject).to eq([])
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe Chef::Knife::Cloud::GoogleService do
|
43
|
-
let(:project) { "test_project" }
|
44
|
-
let(:zone) { "test_zone" }
|
45
|
-
let(:wait_time) { 123 }
|
46
|
-
let(:refresh_rate) { 321 }
|
47
|
-
let(:max_pages) { 456 }
|
48
|
-
let(:max_pages_size) { 654 }
|
49
|
-
let(:connection) { double("connection") }
|
50
|
-
|
51
|
-
let(:service) do
|
52
|
-
Chef::Knife::Cloud::GoogleService.new(
|
53
|
-
project: project,
|
54
|
-
zone: zone,
|
55
|
-
wait_time: wait_time,
|
56
|
-
refresh_rate: refresh_rate,
|
57
|
-
max_pages: max_pages,
|
58
|
-
max_pages_size: max_pages_size
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
62
|
-
before do
|
63
|
-
service.ui = Chef::Knife::UI.new($stdout, $stderr, $stdin, {})
|
64
|
-
allow(service.ui).to receive(:msg)
|
65
|
-
allow(service.ui).to receive(:error)
|
66
|
-
allow(service).to receive(:connection).and_return(connection)
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "#connection" do
|
70
|
-
it "returns a properly configured ComputeService" do
|
71
|
-
compute_service = double("compute_service")
|
72
|
-
client_options = double("client_options")
|
73
|
-
|
74
|
-
allow(service).to receive(:connection).and_call_original
|
75
|
-
|
76
|
-
expect(Google::Apis::ClientOptions).to receive(:new).and_return(client_options)
|
77
|
-
expect(client_options).to receive(:application_name=).with("knife-google")
|
78
|
-
expect(client_options).to receive(:application_version=).with(Knife::Google::VERSION)
|
79
|
-
|
80
|
-
expect(Google::Apis::ComputeV1::ComputeService).to receive(:new).and_return(compute_service)
|
81
|
-
expect(service).to receive(:authorization).and_return("authorization_object")
|
82
|
-
expect(compute_service).to receive(:authorization=).with("authorization_object")
|
83
|
-
expect(compute_service).to receive(:client_options=).with(client_options)
|
84
|
-
|
85
|
-
expect(service.connection).to eq(compute_service)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe "#authorization" do
|
90
|
-
it "returns a Google::Auth authorization object" do
|
91
|
-
auth_object = double("auth_object")
|
92
|
-
expect(Google::Auth).to receive(:get_application_default).and_return(auth_object)
|
93
|
-
expect(service.authorization).to eq(auth_object)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe "#create_server" do
|
98
|
-
it "creates and returns the created instance" do
|
99
|
-
create_instance_obj = double("instance_obj")
|
100
|
-
create_options = { name: "test_instance" }
|
101
|
-
instance = double("instance")
|
102
|
-
|
103
|
-
expect(service).to receive(:validate_server_create_options!).with(create_options)
|
104
|
-
expect(service).to receive(:instance_object_for).with(create_options).and_return(create_instance_obj)
|
105
|
-
expect(connection).to receive(:insert_instance).with(project, zone, create_instance_obj).and_return("operation_id")
|
106
|
-
expect(service).to receive(:wait_for_operation).with("operation_id")
|
107
|
-
expect(service).to receive(:wait_for_status).with("RUNNING")
|
108
|
-
expect(service).to receive(:get_server).with("test_instance").and_return(instance)
|
109
|
-
|
110
|
-
expect(service.create_server(create_options)).to eq(instance)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe "#delete_server" do
|
115
|
-
context "when the instance does not exist" do
|
116
|
-
before do
|
117
|
-
allow(service.ui).to receive(:warn)
|
118
|
-
expect(service).to receive(:get_server).and_raise(Google::Apis::ClientError.new("not found"))
|
119
|
-
end
|
120
|
-
|
121
|
-
it "prints a warning to the user" do
|
122
|
-
expect(service.ui).to receive(:warn).with("Unable to locate instance test_instance in project #{project}, zone #{zone}")
|
123
|
-
|
124
|
-
service.delete_server("test_instance")
|
125
|
-
end
|
126
|
-
|
127
|
-
it "does not attempt to delete the instance" do
|
128
|
-
expect(connection).not_to receive(:delete_instance)
|
129
|
-
|
130
|
-
service.delete_server("test_instance")
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
context "when the instance exists" do
|
135
|
-
it "confirms the deletion and deletes the instance" do
|
136
|
-
instance = double("instance")
|
137
|
-
expect(service).to receive(:get_server).with("test_instance").and_return(instance)
|
138
|
-
expect(service).to receive(:server_summary).with(instance)
|
139
|
-
expect(service.ui).to receive(:confirm)
|
140
|
-
expect(connection).to receive(:delete_instance).with(project, zone, "test_instance").and_return("operation-123")
|
141
|
-
expect(service).to receive(:wait_for_operation).with("operation-123")
|
142
|
-
|
143
|
-
service.delete_server("test_instance")
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
describe "#get_server" do
|
149
|
-
it "returns an instance" do
|
150
|
-
expect(connection).to receive(:get_instance).with(project, zone, "test_instance").and_return("instance")
|
151
|
-
expect(service.get_server("test_instance")).to eq("instance")
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
describe "#list_zones" do
|
156
|
-
subject { service.list_zones }
|
157
|
-
it_behaves_like "a paginated list fetcher", :list_zones, :items, "test_project"
|
158
|
-
end
|
159
|
-
|
160
|
-
describe "#list_disks" do
|
161
|
-
subject { service.list_disks }
|
162
|
-
it_behaves_like "a paginated list fetcher", :list_disks, :items, "test_project", "test_zone"
|
163
|
-
end
|
164
|
-
|
165
|
-
describe "#list_regions" do
|
166
|
-
subject { service.list_regions }
|
167
|
-
it_behaves_like "a paginated list fetcher", :list_regions, :items, "test_project"
|
168
|
-
end
|
169
|
-
|
170
|
-
describe "#list_project_quotas" do
|
171
|
-
let(:response) { double("response") }
|
172
|
-
|
173
|
-
before do
|
174
|
-
expect(service).to receive(:project).and_return(project)
|
175
|
-
expect(connection).to receive(:get_project).with(project).and_return(response)
|
176
|
-
end
|
177
|
-
|
178
|
-
it "returns the results if they exist" do
|
179
|
-
expect(response).to receive(:quotas).and_return("results")
|
180
|
-
expect(service.list_project_quotas).to eq("results")
|
181
|
-
end
|
182
|
-
|
183
|
-
it "returns an empty array if there are no results" do
|
184
|
-
expect(response).to receive(:quotas).and_return(nil)
|
185
|
-
expect(service.list_project_quotas).to eq([])
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
describe "#validate_server_create_options!" do
|
190
|
-
let(:options) do
|
191
|
-
{
|
192
|
-
machine_type: "test_type",
|
193
|
-
network: "test_network",
|
194
|
-
subnet: "test_subnet",
|
195
|
-
public_ip: "public_ip",
|
196
|
-
image: "test_image",
|
197
|
-
image_project: "test_image_project",
|
198
|
-
}
|
199
|
-
end
|
200
|
-
|
201
|
-
before do
|
202
|
-
allow(service).to receive(:valid_machine_type?).and_return(true)
|
203
|
-
allow(service).to receive(:valid_network?).and_return(true)
|
204
|
-
allow(service).to receive(:valid_subnet?).and_return(true)
|
205
|
-
allow(service).to receive(:valid_public_ip_setting?).and_return(true)
|
206
|
-
allow(service).to receive(:image_search_for).and_return(true)
|
207
|
-
end
|
208
|
-
|
209
|
-
it "does not raise an exception when all parameters are supplied and accurate" do
|
210
|
-
expect { service.validate_server_create_options!(options) }.not_to raise_error
|
211
|
-
end
|
212
|
-
|
213
|
-
it "raises an exception if the machine type is not valid" do
|
214
|
-
expect(service).to receive(:valid_machine_type?).with("test_type").and_return(false)
|
215
|
-
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
216
|
-
end
|
217
|
-
|
218
|
-
it "raises an exception if the network is not valid" do
|
219
|
-
expect(service).to receive(:valid_network?).with("test_network").and_return(false)
|
220
|
-
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
221
|
-
end
|
222
|
-
|
223
|
-
it "raises an exception if the network is not valid" do
|
224
|
-
expect(service).to receive(:valid_subnet?).with("test_subnet").and_return(false)
|
225
|
-
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
226
|
-
end
|
227
|
-
|
228
|
-
it "raises an exception if the public ip setting is not valid" do
|
229
|
-
expect(service).to receive(:valid_public_ip_setting?).with("public_ip").and_return(false)
|
230
|
-
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
231
|
-
end
|
232
|
-
|
233
|
-
it "raises an exception if the image parameters are not valid" do
|
234
|
-
expect(service).to receive(:image_search_for).with("test_image", "test_image_project").and_return(nil)
|
235
|
-
expect { service.validate_server_create_options!(options) }.to raise_error(RuntimeError)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
describe "#check_api_call" do
|
240
|
-
it "returns false if the block raises a ClientError" do
|
241
|
-
expect(service.check_api_call { raise Google::Apis::ClientError.new("whoops") }).to eq(false)
|
242
|
-
end
|
243
|
-
|
244
|
-
it "raises an exception if the block raises something other than a ClientError" do
|
245
|
-
expect { service.check_api_call { raise "whoops" } }.to raise_error(RuntimeError)
|
246
|
-
end
|
247
|
-
|
248
|
-
it "returns true if the block does not raise an exception" do
|
249
|
-
expect(service.check_api_call { true }).to eq(true)
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
describe "#valid_machine_type?" do
|
254
|
-
it "returns false if no matchine type was specified" do
|
255
|
-
expect(service.valid_machine_type?(nil)).to eq(false)
|
256
|
-
end
|
257
|
-
|
258
|
-
it "checks the machine type using check_api_call" do
|
259
|
-
expect(connection).to receive(:get_machine_type).with(project, zone, "test_type")
|
260
|
-
expect(service).to receive(:check_api_call).and_call_original
|
261
|
-
|
262
|
-
service.valid_machine_type?("test_type")
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
describe "#valid_network?" do
|
267
|
-
it "returns false if no network was specified" do
|
268
|
-
expect(service.valid_network?(nil)).to eq(false)
|
269
|
-
end
|
270
|
-
|
271
|
-
it "checks the network using check_api_call" do
|
272
|
-
expect(connection).to receive(:get_network).with(project, "test_network")
|
273
|
-
expect(service).to receive(:check_api_call).and_call_original
|
274
|
-
|
275
|
-
service.valid_network?("test_network")
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
describe "#valid_subnet?" do
|
280
|
-
it "returns false if no subnet was specified" do
|
281
|
-
expect(service.valid_subnet?(nil)).to eq(false)
|
282
|
-
end
|
283
|
-
|
284
|
-
it "checks the network using check_api_call" do
|
285
|
-
expect(service).to receive(:region).and_return("test_region")
|
286
|
-
expect(connection).to receive(:get_subnetwork).with(project, "test_region", "test_subnet")
|
287
|
-
expect(service).to receive(:check_api_call).and_call_original
|
288
|
-
|
289
|
-
service.valid_subnet?("test_subnet")
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
describe "#image_exist?" do
|
294
|
-
it "checks the image using check_api_call" do
|
295
|
-
expect(connection).to receive(:get_image).with("image_project", "image_name")
|
296
|
-
expect(service).to receive(:check_api_call).and_call_original
|
297
|
-
|
298
|
-
service.image_exist?("image_project", "image_name")
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
describe "#valid_public_ip_setting?" do
|
303
|
-
it "returns true if the public_ip is nil" do
|
304
|
-
expect(service.valid_public_ip_setting?(nil)).to eq(true)
|
305
|
-
end
|
306
|
-
|
307
|
-
it "returns true if the public_ip is ephemeral" do
|
308
|
-
expect(service.valid_public_ip_setting?("EPHEMERAL")).to eq(true)
|
309
|
-
end
|
310
|
-
|
311
|
-
it "returns true if the public_ip is none" do
|
312
|
-
expect(service.valid_public_ip_setting?("NONE")).to eq(true)
|
313
|
-
end
|
314
|
-
|
315
|
-
it "returns true if the public_ip is a valid IP address" do
|
316
|
-
expect(service).to receive(:valid_ip_address?).with("1.2.3.4").and_return(true)
|
317
|
-
expect(service.valid_public_ip_setting?("1.2.3.4")).to eq(true)
|
318
|
-
end
|
319
|
-
|
320
|
-
it "returns false if it is not nil, ephemeral, none, or a valid IP address" do
|
321
|
-
expect(service).to receive(:valid_ip_address?).with("not_an_ip").and_return(false)
|
322
|
-
expect(service.valid_public_ip_setting?("not_an_ip")).to eq(false)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
describe "#valid_ip_address" do
|
327
|
-
it "returns false if IPAddr is unable to parse the address" do
|
328
|
-
expect(IPAddr).to receive(:new).with("not_an_ip").and_raise(IPAddr::InvalidAddressError)
|
329
|
-
expect(service.valid_ip_address?("not_an_ip")).to eq(false)
|
330
|
-
end
|
331
|
-
|
332
|
-
it "returns true if IPAddr can parse the address" do
|
333
|
-
expect(IPAddr).to receive(:new).with("1.2.3.4")
|
334
|
-
expect(service.valid_ip_address?("1.2.3.4")).to eq(true)
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
describe "#region" do
|
339
|
-
it "returns the region for a given zone" do
|
340
|
-
zone_obj = double("zone_obj", region: "/path/to/test_region")
|
341
|
-
expect(connection).to receive(:get_zone).with(project, zone).and_return(zone_obj)
|
342
|
-
expect(service.region).to eq("test_region")
|
343
|
-
end
|
344
|
-
end
|
345
|
-
|
346
|
-
describe "#instance_object_for" do
|
347
|
-
let(:instance_object) { double("instance_object") }
|
348
|
-
let(:options) do
|
349
|
-
{
|
350
|
-
name: "test_instance",
|
351
|
-
can_ip_forward: "ip_forwarding",
|
352
|
-
machine_type: "test_machine_type",
|
353
|
-
metadata: "test_metadata",
|
354
|
-
tags: "test_tags",
|
355
|
-
}
|
356
|
-
end
|
357
|
-
|
358
|
-
before do
|
359
|
-
expect(service).to receive(:instance_disks_for).with(options).and_return("test_disks")
|
360
|
-
expect(service).to receive(:machine_type_url_for).with("test_machine_type").and_return("test_machine_type_url")
|
361
|
-
expect(service).to receive(:instance_metadata_for).with("test_metadata").and_return("test_metadata_obj")
|
362
|
-
expect(service).to receive(:instance_network_interfaces_for).with(options).and_return("test_network_interfaces")
|
363
|
-
expect(service).to receive(:instance_scheduling_for).with(options).and_return("test_scheduling")
|
364
|
-
allow(service).to receive(:instance_service_accounts_for).with(options).at_least(:once).and_return("test_service_accounts")
|
365
|
-
expect(service).to receive(:instance_tags_for).with("test_tags").and_return("test_tags_obj")
|
366
|
-
end
|
367
|
-
|
368
|
-
it "builds and returns a valid object for creating an instance" do
|
369
|
-
expect(Google::Apis::ComputeV1::Instance).to receive(:new).and_return(instance_object)
|
370
|
-
expect(instance_object).to receive(:name=).with("test_instance")
|
371
|
-
expect(instance_object).to receive(:can_ip_forward=).with("ip_forwarding")
|
372
|
-
expect(instance_object).to receive(:disks=).with("test_disks")
|
373
|
-
expect(instance_object).to receive(:machine_type=).with("test_machine_type_url")
|
374
|
-
expect(instance_object).to receive(:metadata=).with("test_metadata_obj")
|
375
|
-
expect(instance_object).to receive(:network_interfaces=).with("test_network_interfaces")
|
376
|
-
expect(instance_object).to receive(:scheduling=).with("test_scheduling")
|
377
|
-
expect(instance_object).to receive(:service_accounts=).with("test_service_accounts")
|
378
|
-
expect(instance_object).to receive(:tags=).with("test_tags_obj")
|
379
|
-
|
380
|
-
expect(service.instance_object_for(options)).to eq(instance_object)
|
381
|
-
end
|
382
|
-
|
383
|
-
it "does not include service accounts if none exist" do
|
384
|
-
expect(service).to receive(:instance_service_accounts_for).with(options).and_return(nil)
|
385
|
-
expect(instance_object).not_to receive(:service_accounts=)
|
386
|
-
|
387
|
-
service.instance_object_for(options)
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
describe "#instance_disks_for" do
|
392
|
-
|
393
|
-
before do
|
394
|
-
expect(service).to receive(:instance_boot_disk_for).with(options).and_return("boot_disk")
|
395
|
-
end
|
396
|
-
|
397
|
-
context "when no additional disks are to be attached" do
|
398
|
-
let(:options) { { additional_disks: [] } }
|
399
|
-
|
400
|
-
it "returns an array containing only the boot disk" do
|
401
|
-
expect(service.instance_disks_for(options)).to eq(%w{boot_disk})
|
402
|
-
end
|
403
|
-
end
|
404
|
-
|
405
|
-
context "when additional disks are to be attached and they exist" do
|
406
|
-
let(:options) { { additional_disks: %w{disk1 disk2} } }
|
407
|
-
|
408
|
-
it "returns an array containing all three disks" do
|
409
|
-
disk1 = double("disk1")
|
410
|
-
disk2 = double("disk2")
|
411
|
-
attached_disk1 = double("attached_disk1")
|
412
|
-
attached_disk2 = double("attached_disk2")
|
413
|
-
|
414
|
-
expect(connection).to receive(:get_disk).with(project, zone, "disk1").and_return(disk1)
|
415
|
-
expect(connection).to receive(:get_disk).with(project, zone, "disk2").and_return(disk2)
|
416
|
-
expect(disk1).to receive(:self_link).and_return("disk1_url")
|
417
|
-
expect(disk2).to receive(:self_link).and_return("disk2_url")
|
418
|
-
expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(attached_disk1)
|
419
|
-
expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(attached_disk2)
|
420
|
-
expect(attached_disk1).to receive(:source=).and_return("disk1_url")
|
421
|
-
expect(attached_disk2).to receive(:source=).and_return("disk2_url")
|
422
|
-
expect(service.instance_disks_for(options)).to eq(["boot_disk", attached_disk1, attached_disk2])
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
context "when an additional disk is to be attached but does not exist" do
|
427
|
-
let(:options) { { additional_disks: %w{bad_disk} } }
|
428
|
-
|
429
|
-
it "raises an exception" do
|
430
|
-
expect(connection).to receive(:get_disk).with(project, zone, "bad_disk").and_raise(Google::Apis::ClientError.new("disk not found"))
|
431
|
-
expect(service.ui).to receive(:error).with("Unable to attach disk bad_disk to the instance: disk not found")
|
432
|
-
expect { service.instance_disks_for(options) }.to raise_error(Google::Apis::ClientError)
|
433
|
-
end
|
434
|
-
end
|
435
|
-
end
|
436
|
-
|
437
|
-
describe "#instance_boot_disk_for" do
|
438
|
-
it "sets up a disk object and returns it" do
|
439
|
-
disk = double("disk")
|
440
|
-
params = double("params")
|
441
|
-
options = {
|
442
|
-
boot_disk_autodelete: "autodelete_param",
|
443
|
-
boot_disk_size: "disk_size",
|
444
|
-
boot_disk_ssd: "disk_ssd",
|
445
|
-
image: "disk_image",
|
446
|
-
image_project: "disk_image_project",
|
447
|
-
}
|
448
|
-
|
449
|
-
expect(service).to receive(:boot_disk_name_for).with(options).and_return("disk_name")
|
450
|
-
expect(service).to receive(:boot_disk_type_for).with(options).and_return("disk_type")
|
451
|
-
expect(service).to receive(:disk_type_url_for).with("disk_type").and_return("disk_type_url")
|
452
|
-
expect(service).to receive(:image_search_for).with("disk_image", "disk_image_project").and_return("source_image")
|
453
|
-
|
454
|
-
expect(Google::Apis::ComputeV1::AttachedDisk).to receive(:new).and_return(disk)
|
455
|
-
expect(Google::Apis::ComputeV1::AttachedDiskInitializeParams).to receive(:new).and_return(params)
|
456
|
-
expect(disk).to receive(:boot=).with(true)
|
457
|
-
expect(disk).to receive(:auto_delete=).with("autodelete_param")
|
458
|
-
expect(disk).to receive(:initialize_params=).with(params)
|
459
|
-
expect(params).to receive(:disk_name=).with("disk_name")
|
460
|
-
expect(params).to receive(:disk_size_gb=).with("disk_size")
|
461
|
-
expect(params).to receive(:disk_type=).with("disk_type_url")
|
462
|
-
expect(params).to receive(:source_image=).with("source_image")
|
463
|
-
|
464
|
-
expect(service.instance_boot_disk_for(options)).to eq(disk)
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
describe "#boot_disk_type_for" do
|
469
|
-
it "returns pd-ssd if boot_disk_ssd is true" do
|
470
|
-
expect(service.boot_disk_type_for(boot_disk_ssd: true)).to eq("pd-ssd")
|
471
|
-
end
|
472
|
-
|
473
|
-
it "returns pd-standard if boot_disk_ssd is false" do
|
474
|
-
expect(service.boot_disk_type_for(boot_disk_ssd: false)).to eq("pd-standard")
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
describe "#image_search_for" do
|
479
|
-
context "when the user supplies an image project" do
|
480
|
-
it "returns the image URL based on the image project" do
|
481
|
-
expect(service).to receive(:image_url_for).with("test_project", "test_image").and_return("image_url")
|
482
|
-
expect(service.image_search_for("test_image", "test_project")).to eq("image_url")
|
483
|
-
end
|
484
|
-
end
|
485
|
-
|
486
|
-
context "when the user does not supply an image project" do
|
487
|
-
context "when the image provided is an alias" do
|
488
|
-
it "returns the alias URL" do
|
489
|
-
expect(service).to receive(:image_alias_url).with("test_image").and_return("image_alias_url")
|
490
|
-
expect(service.image_search_for("test_image", nil)).to eq("image_alias_url")
|
491
|
-
end
|
492
|
-
end
|
493
|
-
|
494
|
-
context "when the image provided is not an alias" do
|
495
|
-
before do
|
496
|
-
expect(service).to receive(:image_alias_url).and_return(nil)
|
497
|
-
end
|
498
|
-
|
499
|
-
context "when the image exists in the user's project" do
|
500
|
-
it "returns the image URL" do
|
501
|
-
expect(service).to receive(:image_url_for).with(project, "test_image").and_return("image_url")
|
502
|
-
expect(service.image_search_for("test_image", nil)).to eq("image_url")
|
503
|
-
end
|
504
|
-
end
|
505
|
-
|
506
|
-
context "when the image does not exist in the user's project" do
|
507
|
-
before do
|
508
|
-
expect(service).to receive(:image_url_for).with(project, "test_image").and_return(nil)
|
509
|
-
end
|
510
|
-
|
511
|
-
context "when the image matches a known public project" do
|
512
|
-
it "returns the image URL from the public project" do
|
513
|
-
expect(service).to receive(:public_project_for_image).with("test_image").and_return("public_project")
|
514
|
-
expect(service).to receive(:image_url_for).with("public_project", "test_image").and_return("image_url")
|
515
|
-
expect(service.image_search_for("test_image", nil)).to eq("image_url")
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
context "when the image does not match a known project" do
|
520
|
-
it "returns nil" do
|
521
|
-
expect(service).to receive(:public_project_for_image).with("test_image").and_return(nil)
|
522
|
-
expect(service).not_to receive(:image_url_for)
|
523
|
-
expect(service.image_search_for("test_image", nil)).to eq(nil)
|
524
|
-
end
|
525
|
-
end
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
end
|
530
|
-
|
531
|
-
describe "#image_url_for" do
|
532
|
-
it "returns nil if the image does not exist" do
|
533
|
-
expect(service).to receive(:image_exist?).with("image_project", "image_name").and_return(false)
|
534
|
-
expect(service.image_url_for("image_project", "image_name")).to eq(nil)
|
535
|
-
end
|
536
|
-
|
537
|
-
it "returns a properly formatted image URL if the image exists" do
|
538
|
-
expect(service).to receive(:image_exist?).with("image_project", "image_name").and_return(true)
|
539
|
-
expect(service.image_url_for("image_project", "image_name")).to eq("projects/image_project/global/images/image_name")
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
describe "#image_alias_url" do
|
544
|
-
context "when the image_alias is not a valid alias" do
|
545
|
-
it "returns nil" do
|
546
|
-
expect(service.image_alias_url("fake_alias")).to eq(nil)
|
547
|
-
end
|
548
|
-
end
|
549
|
-
|
550
|
-
context "when the image_alias is a valid alias" do
|
551
|
-
before do
|
552
|
-
allow(connection).to receive(:list_images).and_return(response)
|
553
|
-
end
|
554
|
-
|
555
|
-
context "when the response contains no images" do
|
556
|
-
let(:response) { double("response", items: []) }
|
557
|
-
|
558
|
-
it "returns nil" do
|
559
|
-
expect(service.image_alias_url("centos-7")).to eq(nil)
|
560
|
-
end
|
561
|
-
end
|
562
|
-
|
563
|
-
context "when the response contains images but none match the name" do
|
564
|
-
let(:image1) { double("image1", name: "centos-6") }
|
565
|
-
let(:image2) { double("image2", name: "centos-6") }
|
566
|
-
let(:image3) { double("image3", name: "ubuntu-14") }
|
567
|
-
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
568
|
-
|
569
|
-
it "returns nil" do
|
570
|
-
expect(service.image_alias_url("centos-7")).to eq(nil)
|
571
|
-
end
|
572
|
-
end
|
573
|
-
|
574
|
-
context "when the response contains images that match the name" do
|
575
|
-
let(:image1) { double("image1", name: "centos-7-v20160201", self_link: "image1_selflink") }
|
576
|
-
let(:image2) { double("image2", name: "centos-7-v20160301", self_link: "image2_selflink") }
|
577
|
-
let(:image3) { double("image3", name: "centos-6", self_link: "image3_selflink") }
|
578
|
-
let(:response) { double("response", items: [ image1, image2, image3 ]) }
|
579
|
-
|
580
|
-
it "returns the link for image2 which is the most recent image" do
|
581
|
-
expect(service.image_alias_url("centos-7")).to eq("image2_selflink")
|
582
|
-
end
|
583
|
-
end
|
584
|
-
end
|
585
|
-
end
|
586
|
-
|
587
|
-
describe "#boot_disk_name_for" do
|
588
|
-
it "returns the boot disk name if supplied by the user" do
|
589
|
-
options = { name: "instance_name", boot_disk_name: "disk_name" }
|
590
|
-
expect(service.boot_disk_name_for(options)).to eq("disk_name")
|
591
|
-
end
|
592
|
-
|
593
|
-
it "returns the instance name if the boot disk name is not supplied" do
|
594
|
-
options = { name: "instance_name" }
|
595
|
-
expect(service.boot_disk_name_for(options)).to eq("instance_name")
|
596
|
-
end
|
597
|
-
end
|
598
|
-
|
599
|
-
describe "#machine_type_url_for" do
|
600
|
-
it "returns a properly-formatted machine type URL" do
|
601
|
-
expect(service.machine_type_url_for("test_type")).to eq("zones/test_zone/machineTypes/test_type")
|
602
|
-
end
|
603
|
-
end
|
604
|
-
|
605
|
-
describe "#instance_metadata_for" do
|
606
|
-
it "returns nil if the passed-in metadata is nil" do
|
607
|
-
expect(service.instance_metadata_for(nil)).to eq(nil)
|
608
|
-
end
|
609
|
-
|
610
|
-
it "returns nil if the passed-in metadata is empty" do
|
611
|
-
expect(service.instance_metadata_for([])).to eq(nil)
|
612
|
-
end
|
613
|
-
|
614
|
-
it "returns a properly-formatted metadata object if metadata is passed in" do
|
615
|
-
metadata = { "key1" => "value1", "key2" => "value2" }
|
616
|
-
metadata_obj = double("metadata_obj")
|
617
|
-
item_1 = double("item_1")
|
618
|
-
item_2 = double("item_2")
|
619
|
-
|
620
|
-
expect(Google::Apis::ComputeV1::Metadata).to receive(:new).and_return(metadata_obj)
|
621
|
-
expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item_1)
|
622
|
-
expect(Google::Apis::ComputeV1::Metadata::Item).to receive(:new).and_return(item_2)
|
623
|
-
expect(item_1).to receive(:key=).with("key1")
|
624
|
-
expect(item_1).to receive(:value=).with("value1")
|
625
|
-
expect(item_2).to receive(:key=).with("key2")
|
626
|
-
expect(item_2).to receive(:value=).with("value2")
|
627
|
-
expect(metadata_obj).to receive(:items=).with([item_1, item_2])
|
628
|
-
|
629
|
-
expect(service.instance_metadata_for(metadata)).to eq(metadata_obj)
|
630
|
-
end
|
631
|
-
end
|
632
|
-
|
633
|
-
describe "#instance_network_interfaces_for" do
|
634
|
-
let(:interface) { double("interface" ) }
|
635
|
-
let(:options) { { network: "test_network", public_ip: "public_ip" } }
|
636
|
-
|
637
|
-
before do
|
638
|
-
allow(service).to receive(:network_url_for)
|
639
|
-
allow(service).to receive(:subnet_url_for)
|
640
|
-
allow(service).to receive(:instance_access_configs_for)
|
641
|
-
allow(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
|
642
|
-
allow(interface).to receive(:network=)
|
643
|
-
allow(interface).to receive(:subnetwork=)
|
644
|
-
allow(interface).to receive(:access_configs=)
|
645
|
-
end
|
646
|
-
|
647
|
-
it "creates a network interface object and returns it" do
|
648
|
-
expect(Google::Apis::ComputeV1::NetworkInterface).to receive(:new).and_return(interface)
|
649
|
-
expect(service.instance_network_interfaces_for(options)).to eq([interface])
|
650
|
-
end
|
651
|
-
|
652
|
-
it "sets the network" do
|
653
|
-
expect(service).to receive(:network_url_for).with("test_network").and_return("network_url")
|
654
|
-
expect(interface).to receive(:network=).with("network_url")
|
655
|
-
service.instance_network_interfaces_for(options)
|
656
|
-
end
|
657
|
-
|
658
|
-
it "sets the access configs" do
|
659
|
-
expect(service).to receive(:instance_access_configs_for).with("public_ip").and_return("access_configs")
|
660
|
-
expect(interface).to receive(:access_configs=).with("access_configs")
|
661
|
-
service.instance_network_interfaces_for(options)
|
662
|
-
end
|
663
|
-
|
664
|
-
it "does not set a subnetwork" do
|
665
|
-
expect(service).not_to receive(:subnet_url_for)
|
666
|
-
expect(interface).not_to receive(:subnetwork=)
|
667
|
-
service.instance_network_interfaces_for(options)
|
668
|
-
end
|
669
|
-
|
670
|
-
context "when a subnet exists" do
|
671
|
-
let(:options) { { network: "test_network", subnet: "test_subnet", public_ip: "public_ip" } }
|
672
|
-
|
673
|
-
it "sets the subnetwork" do
|
674
|
-
expect(service).to receive(:subnet_url_for).with("test_subnet").and_return("subnet_url")
|
675
|
-
expect(interface).to receive(:subnetwork=).with("subnet_url")
|
676
|
-
service.instance_network_interfaces_for(options)
|
677
|
-
end
|
678
|
-
end
|
679
|
-
end
|
680
|
-
|
681
|
-
describe "#instance_access_configs_for" do
|
682
|
-
let(:interface) { double("interface" ) }
|
683
|
-
|
684
|
-
context "for None public_ip" do
|
685
|
-
it "empty public_ip none|None|NONE|~" do
|
686
|
-
expect(service.instance_access_configs_for("none")).to eq([])
|
687
|
-
|
688
|
-
expect(service.instance_access_configs_for("None")).to eq([])
|
689
|
-
|
690
|
-
expect(service.instance_access_configs_for("NONE")).to eq([])
|
691
|
-
end
|
692
|
-
end
|
693
|
-
|
694
|
-
context "for valid public_ip" do
|
695
|
-
it "valid public_ip" do
|
696
|
-
access_config = service.instance_access_configs_for("8.8.8.8")
|
697
|
-
expect(access_config.first.nat_ip).to eq("8.8.8.8")
|
698
|
-
end
|
699
|
-
end
|
700
|
-
|
701
|
-
context "for invalid public_ip" do
|
702
|
-
it "empty public_ip none|None|NONE|~" do
|
703
|
-
access_config = service.instance_access_configs_for("oh no not a valid IP")
|
704
|
-
expect(access_config.first.nat_ip).to eq(nil)
|
705
|
-
end
|
706
|
-
end
|
707
|
-
end
|
708
|
-
|
709
|
-
describe "#network_url_for" do
|
710
|
-
it "returns a properly-formatted network URL" do
|
711
|
-
expect(service.network_url_for("test_network")).to eq("projects/test_project/global/networks/test_network")
|
712
|
-
end
|
713
|
-
end
|
714
|
-
|
715
|
-
describe "#subnet_url_for" do
|
716
|
-
it "returns a properly-formatted subnet URL" do
|
717
|
-
expect(service).to receive(:region).and_return("test_region")
|
718
|
-
expect(service.subnet_url_for("test_subnet")).to eq("projects/test_project/regions/test_region/subnetworks/test_subnet")
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
|
-
describe "#instance_scheduling_for" do
|
723
|
-
it "returns a properly-formatted scheduling object" do
|
724
|
-
scheduling = double("scheduling")
|
725
|
-
options = { auto_restart: "auto_restart", auto_migrate: "auto_migrate", preemptible: "preempt" }
|
726
|
-
|
727
|
-
expect(service).to receive(:migrate_setting_for).with("auto_migrate").and_return("host_maintenance")
|
728
|
-
expect(Google::Apis::ComputeV1::Scheduling).to receive(:new).and_return(scheduling)
|
729
|
-
expect(scheduling).to receive(:automatic_restart=).with("auto_restart")
|
730
|
-
expect(scheduling).to receive(:on_host_maintenance=).with("host_maintenance")
|
731
|
-
expect(scheduling).to receive(:preemptible=).with("preempt")
|
732
|
-
|
733
|
-
expect(service.instance_scheduling_for(options)).to eq(scheduling)
|
734
|
-
end
|
735
|
-
end
|
736
|
-
|
737
|
-
describe "#migrate_setting_for" do
|
738
|
-
it "returns MIGRATE when auto_migrate is true" do
|
739
|
-
expect(service.migrate_setting_for(true)).to eq("MIGRATE")
|
740
|
-
end
|
741
|
-
|
742
|
-
it "returns TERMINATE when auto_migrate is false" do
|
743
|
-
expect(service.migrate_setting_for(false)).to eq("TERMINATE")
|
744
|
-
end
|
745
|
-
end
|
746
|
-
|
747
|
-
describe "#instance_service_accounts_for" do
|
748
|
-
it "returns nil if service_account_scopes is nil" do
|
749
|
-
expect(service.instance_service_accounts_for({})).to eq(nil)
|
750
|
-
end
|
751
|
-
|
752
|
-
it "returns nil if service_account_scopes is empty" do
|
753
|
-
expect(service.instance_service_accounts_for({ service_account_scopes: [] })).to eq(nil)
|
754
|
-
end
|
755
|
-
|
756
|
-
it "returns an array containing a properly-formatted service account" do
|
757
|
-
service_account = double("service_account")
|
758
|
-
options = { service_account_name: "account_name", service_account_scopes: %w{scope1 scope2} }
|
759
|
-
|
760
|
-
expect(Google::Apis::ComputeV1::ServiceAccount).to receive(:new).and_return(service_account)
|
761
|
-
expect(service_account).to receive(:email=).with("account_name")
|
762
|
-
expect(service).to receive(:service_account_scope_url).with("scope1").and_return("https://www.googleapis.com/auth/scope1")
|
763
|
-
expect(service).to receive(:service_account_scope_url).with("scope2").and_return("https://www.googleapis.com/auth/scope2")
|
764
|
-
expect(service_account).to receive(:scopes=).with([
|
765
|
-
"https://www.googleapis.com/auth/scope1",
|
766
|
-
"https://www.googleapis.com/auth/scope2",
|
767
|
-
])
|
768
|
-
|
769
|
-
expect(service.instance_service_accounts_for(options)).to eq([service_account])
|
770
|
-
end
|
771
|
-
end
|
772
|
-
|
773
|
-
describe "#service_account_scope_url" do
|
774
|
-
it "returns the passed-in scope if it already looks like a scope URL" do
|
775
|
-
scope = "https://www.googleapis.com/auth/fake_scope"
|
776
|
-
expect(service.service_account_scope_url(scope)).to eq(scope)
|
777
|
-
end
|
778
|
-
|
779
|
-
it "returns a properly-formatted scope URL if a short-name or alias is provided" do
|
780
|
-
expect(service).to receive(:translate_scope_alias).with("scope_alias").and_return("real_scope")
|
781
|
-
expect(service.service_account_scope_url("scope_alias")).to eq("https://www.googleapis.com/auth/real_scope")
|
782
|
-
end
|
783
|
-
end
|
784
|
-
|
785
|
-
describe "#translate_scope_alias" do
|
786
|
-
it "returns a scope for a given alias" do
|
787
|
-
expect(service.translate_scope_alias("storage-rw")).to eq("devstorage.read_write")
|
788
|
-
end
|
789
|
-
|
790
|
-
it "returns the passed-in scope alias if nothing matches in the alias map" do
|
791
|
-
expect(service.translate_scope_alias("fake_scope")).to eq("fake_scope")
|
792
|
-
end
|
793
|
-
end
|
794
|
-
|
795
|
-
describe "#instance_tags_for" do
|
796
|
-
it "returns nil if tags is nil" do
|
797
|
-
expect(service.instance_tags_for(nil)).to eq(nil)
|
798
|
-
end
|
799
|
-
|
800
|
-
it "returns nil if tags is empty" do
|
801
|
-
expect(service.instance_tags_for([])).to eq(nil)
|
802
|
-
end
|
803
|
-
|
804
|
-
it "returns a properly-formatted tags object" do
|
805
|
-
tags_obj = double("tags_obj")
|
806
|
-
|
807
|
-
expect(Google::Apis::ComputeV1::Tags).to receive(:new).and_return(tags_obj)
|
808
|
-
expect(tags_obj).to receive(:items=).with("test_tags")
|
809
|
-
|
810
|
-
expect(service.instance_tags_for("test_tags")).to eq(tags_obj)
|
811
|
-
end
|
812
|
-
end
|
813
|
-
|
814
|
-
describe "#network_for" do
|
815
|
-
it "returns the network name if it exists" do
|
816
|
-
interface = double("interface", network: "/some/path/to/default_network")
|
817
|
-
instance = double("instance", network_interfaces: [interface])
|
818
|
-
|
819
|
-
expect(service.network_for(instance)).to eq("default_network")
|
820
|
-
end
|
821
|
-
|
822
|
-
it "returns 'unknown' if the network cannot be found" do
|
823
|
-
instance = double("instance")
|
824
|
-
expect(instance).to receive(:network_interfaces).and_raise(NoMethodError)
|
825
|
-
|
826
|
-
expect(service.network_for(instance)).to eq("unknown")
|
827
|
-
end
|
828
|
-
end
|
829
|
-
|
830
|
-
describe "#machine_type_for" do
|
831
|
-
it "returns the machine type name" do
|
832
|
-
instance = double("instance", machine_type: "/some/path/to/test_type")
|
833
|
-
expect(service.machine_type_for(instance)).to eq("test_type")
|
834
|
-
end
|
835
|
-
end
|
836
|
-
|
837
|
-
describe "#public_project_for_image" do
|
838
|
-
{
|
839
|
-
"centos" => "centos-cloud",
|
840
|
-
"container-vm" => "google-containers",
|
841
|
-
"coreos" => "coreos-cloud",
|
842
|
-
"debian" => "debian-cloud",
|
843
|
-
"opensuse-cloud" => "opensuse-cloud",
|
844
|
-
"rhel" => "rhel-cloud",
|
845
|
-
"sles" => "suse-cloud",
|
846
|
-
"ubuntu" => "ubuntu-os-cloud",
|
847
|
-
"windows" => "windows-cloud",
|
848
|
-
}.each do |image_name, project_name|
|
849
|
-
it "returns project #{project_name} for an image named #{image_name}" do
|
850
|
-
expect(service.public_project_for_image(image_name)).to eq(project_name)
|
851
|
-
end
|
852
|
-
end
|
853
|
-
end
|
854
|
-
|
855
|
-
describe "#disk_type_url_for" do
|
856
|
-
it "returns a properly-formatted disk type URL" do
|
857
|
-
expect(service.disk_type_url_for("disk_type")).to eq("zones/test_zone/diskTypes/disk_type")
|
858
|
-
end
|
859
|
-
end
|
860
|
-
|
861
|
-
describe "#paginated_results" do
|
862
|
-
let(:response) { double("response") }
|
863
|
-
let(:api_method) { :list_stuff }
|
864
|
-
let(:items_method) { :items }
|
865
|
-
let(:args) { %w{arg1 arg2} }
|
866
|
-
let(:max_pages) { 5 }
|
867
|
-
let(:max_page_size) { 100 }
|
868
|
-
|
869
|
-
subject { service.paginated_results(api_method, items_method, *args) }
|
870
|
-
|
871
|
-
before do
|
872
|
-
allow(response).to receive(:next_page_token)
|
873
|
-
allow(service).to receive(:max_pages).and_return(max_pages)
|
874
|
-
allow(service).to receive(:max_page_size).and_return(max_page_size)
|
875
|
-
end
|
876
|
-
|
877
|
-
context "when the response has no items" do
|
878
|
-
it "returns an empty array" do
|
879
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response)
|
880
|
-
expect(response).to receive(:items).and_return(nil)
|
881
|
-
expect(subject).to eq([])
|
882
|
-
end
|
883
|
-
end
|
884
|
-
|
885
|
-
context "when the response has items with no additional pages" do
|
886
|
-
it "calls the API once and returns the fetched results" do
|
887
|
-
expect(response).to receive(:items).and_return(%w{item1 item2})
|
888
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response)
|
889
|
-
expect(subject).to eq(%w{item1 item2})
|
890
|
-
end
|
891
|
-
end
|
892
|
-
|
893
|
-
context "when the response has items spanning 3 pages" do
|
894
|
-
|
895
|
-
it "calls the API 3 times and returns the results" do
|
896
|
-
response1 = double("response1", items: %w{item1 item2}, next_page_token: "page2")
|
897
|
-
response2 = double("response2", items: %w{item3 item4}, next_page_token: "page3")
|
898
|
-
response3 = double("response3", items: %w{item5 item6}, next_page_token: nil)
|
899
|
-
|
900
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response1)
|
901
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page2").and_return(response2)
|
902
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page3").and_return(response3)
|
903
|
-
expect(subject).to eq(%w{item1 item2 item3 item4 item5 item6})
|
904
|
-
end
|
905
|
-
end
|
906
|
-
|
907
|
-
context "when the response has items spanning more than max allowed pages" do
|
908
|
-
it "only calls the API the maximum allow number of times and returns results" do
|
909
|
-
response1 = double("response1", items: %w{item1}, next_page_token: "page2")
|
910
|
-
response2 = double("response2", items: %w{item2}, next_page_token: "page3")
|
911
|
-
response3 = double("response3", items: %w{item3}, next_page_token: "page4")
|
912
|
-
response4 = double("response4", items: %w{item4}, next_page_token: "page5")
|
913
|
-
response5 = double("response5", items: %w{item5}, next_page_token: "page6")
|
914
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: nil).and_return(response1)
|
915
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page2").and_return(response2)
|
916
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page3").and_return(response3)
|
917
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page4").and_return(response4)
|
918
|
-
expect(connection).to receive(:list_stuff).with(*args, max_results: max_page_size, page_token: "page5").and_return(response5)
|
919
|
-
expect(service.ui).to receive(:warn).with("Max pages (5) reached, but more results exist - truncating results...")
|
920
|
-
expect(subject).to eq(%w{item1 item2 item3 item4 item5})
|
921
|
-
end
|
922
|
-
end
|
923
|
-
end
|
924
|
-
|
925
|
-
describe "#wait_for_status" do
|
926
|
-
let(:item) { double("item") }
|
927
|
-
|
928
|
-
before do
|
929
|
-
allow(service).to receive(:wait_time).and_return(600)
|
930
|
-
allow(service).to receive(:refresh_rate).and_return(2)
|
931
|
-
|
932
|
-
# muffle any stdout output from this method
|
933
|
-
allow(service).to receive(:print)
|
934
|
-
|
935
|
-
# don"t actually sleep
|
936
|
-
allow(service).to receive(:sleep)
|
937
|
-
end
|
938
|
-
|
939
|
-
context "when the items completes normally, 3 loops" do
|
940
|
-
it "only refreshes the item 3 times" do
|
941
|
-
allow(item).to receive(:status).exactly(3).times.and_return("PENDING", "RUNNING", "DONE")
|
942
|
-
|
943
|
-
service.wait_for_status("DONE") { item }
|
944
|
-
end
|
945
|
-
end
|
946
|
-
|
947
|
-
context "when the item is completed on the first loop" do
|
948
|
-
it "only refreshes the item 1 time" do
|
949
|
-
allow(item).to receive(:status).once.and_return("DONE")
|
950
|
-
|
951
|
-
service.wait_for_status("DONE") { item }
|
952
|
-
end
|
953
|
-
end
|
954
|
-
|
955
|
-
context "when the timeout is exceeded" do
|
956
|
-
it "prints a warning and exits" do
|
957
|
-
allow(Timeout).to receive(:timeout).and_raise(Timeout::Error)
|
958
|
-
expect(service.ui).to receive(:error)
|
959
|
-
.with("Request did not complete in 600 seconds. Check the Google Cloud Console for more info.")
|
960
|
-
expect { service.wait_for_status("DONE") { item } }.to raise_error(SystemExit)
|
961
|
-
end
|
962
|
-
end
|
963
|
-
|
964
|
-
context "when a non-timeout exception is raised" do
|
965
|
-
it "raises the original exception" do
|
966
|
-
allow(item).to receive(:status).and_raise(RuntimeError)
|
967
|
-
expect { service.wait_for_status("DONE") { item } }.to raise_error(RuntimeError)
|
968
|
-
end
|
969
|
-
end
|
970
|
-
end
|
971
|
-
|
972
|
-
describe "#wait_for_operation" do
|
973
|
-
let(:operation) { double("operation", name: "operation-123") }
|
974
|
-
|
975
|
-
it "raises a properly-formatted exception when errors exist" do
|
976
|
-
error1 = double("error1", code: "ERROR1", message: "error 1")
|
977
|
-
error2 = double("error2", code: "ERROR2", message: "error 2")
|
978
|
-
expect(service).to receive(:wait_for_status).with("DONE")
|
979
|
-
expect(service).to receive(:operation_errors).with("operation-123").and_return([error1, error2])
|
980
|
-
expect(service.ui).to receive(:error).with("#{service.ui.color("ERROR1", :bold)}: error 1")
|
981
|
-
expect(service.ui).to receive(:error).with("#{service.ui.color("ERROR2", :bold)}: error 2")
|
982
|
-
|
983
|
-
expect { service.wait_for_operation(operation) }.to raise_error(RuntimeError, "Operation operation-123 failed.")
|
984
|
-
end
|
985
|
-
|
986
|
-
it "does not raise an exception if no errors are encountered" do
|
987
|
-
expect(service).to receive(:wait_for_status).with("DONE")
|
988
|
-
expect(service).to receive(:operation_errors).with("operation-123").and_return([])
|
989
|
-
expect(service.ui).not_to receive(:error)
|
990
|
-
|
991
|
-
expect { service.wait_for_operation(operation) }.not_to raise_error
|
992
|
-
end
|
993
|
-
end
|
994
|
-
|
995
|
-
describe "#zone_operation" do
|
996
|
-
it "fetches the operation from the API and returns it" do
|
997
|
-
expect(connection).to receive(:get_zone_operation).with(project, zone, "operation-123").and_return("operation")
|
998
|
-
expect(service.zone_operation("operation-123")).to eq("operation")
|
999
|
-
end
|
1000
|
-
end
|
1001
|
-
|
1002
|
-
describe "#operation_errors" do
|
1003
|
-
let(:operation) { double("operation") }
|
1004
|
-
let(:error_obj) { double("error_obj") }
|
1005
|
-
|
1006
|
-
before do
|
1007
|
-
expect(service).to receive(:zone_operation).with("operation-123").and_return(operation)
|
1008
|
-
end
|
1009
|
-
|
1010
|
-
it "returns an empty array if there are no errors" do
|
1011
|
-
expect(operation).to receive(:error).and_return(nil)
|
1012
|
-
expect(service.operation_errors("operation-123")).to eq([])
|
1013
|
-
end
|
1014
|
-
|
1015
|
-
it "returns the errors from the operation if they exist" do
|
1016
|
-
expect(operation).to receive(:error).twice.and_return(error_obj)
|
1017
|
-
expect(error_obj).to receive(:errors).and_return("some errors")
|
1018
|
-
expect(service.operation_errors("operation-123")).to eq("some errors")
|
1019
|
-
end
|
1020
|
-
end
|
1021
|
-
end
|