knife-google 1.0.0 → 1.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.
- data/.travis.yml +4 -0
- data/README.md +34 -7
- data/lib/chef/knife/google_base.rb +1 -0
- data/lib/chef/knife/google_server_create.rb +49 -8
- data/lib/chef/knife/google_server_delete.rb +9 -7
- data/lib/chef/knife/google_server_list.rb +15 -15
- data/lib/google/compute/client.rb +26 -18
- data/lib/google/compute/creatable_resource_collection.rb +6 -0
- data/lib/google/compute/exception.rb +2 -0
- data/lib/knife-google/version.rb +1 -1
- data/spec/chef/knife/google_base_spec.rb +1 -1
- data/spec/chef/knife/google_server_create_spec.rb +36 -16
- data/spec/chef/knife/google_server_delete_spec.rb +22 -0
- data/spec/chef/knife/google_server_list_spec.rb +13 -14
- data/spec/data/{compute-v1beta14.json → compute-v1beta15.json} +4122 -1228
- data/spec/data/disk.json +2 -2
- data/spec/data/firewall.json +2 -2
- data/spec/data/global_operation.json +3 -3
- data/spec/data/image.json +2 -2
- data/spec/data/kernel.json +1 -1
- data/spec/data/machine_type.json +3 -3
- data/spec/data/network.json +1 -1
- data/spec/data/project.json +1 -1
- data/spec/data/serial_port_output.json +1 -1
- data/spec/data/server.json +6 -6
- data/spec/data/snapshot.json +2 -2
- data/spec/data/zone.json +1 -1
- data/spec/data/zone_operation.json +3 -3
- data/spec/google/compute/disk_spec.rb +17 -7
- data/spec/google/compute/firewall_spec.rb +27 -26
- data/spec/google/compute/global_operation_spec.rb +6 -6
- data/spec/google/compute/image_spec.rb +11 -11
- data/spec/google/compute/kernel_spec.rb +4 -4
- data/spec/google/compute/machine_type_spec.rb +4 -4
- data/spec/google/compute/network_spec.rb +9 -9
- data/spec/google/compute/project_spec.rb +14 -14
- data/spec/google/compute/server_spec.rb +23 -23
- data/spec/google/compute/snapshot_spec.rb +6 -18
- data/spec/google/compute/zone_operation_spec.rb +6 -6
- data/spec/google/compute/zone_spec.rb +5 -4
- data/spec/support/mocks.rb +2 -2
- metadata +112 -117
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -65,6 +65,19 @@ or, for Gemfile:
|
|
65
65
|
gem 'knife-google'
|
66
66
|
```
|
67
67
|
|
68
|
+
There is a long standing issue in Ruby where the net/http library by default does not check the validity of an SSL certificate during a TLS handshake.
|
69
|
+
|
70
|
+
To configure Windows system to validate SSL certificate please download
|
71
|
+
[cacert.pem](http://curl.haxx.se/ca/cacert.pem) file and save to C: drive. Now make ruby aware of your certificate authority by setting SSL_CERT_FILE.
|
72
|
+
|
73
|
+
To set this in your current command prompt session, type:
|
74
|
+
|
75
|
+
```sh
|
76
|
+
set SSL_CERT_FILE = C:\cacert.pem
|
77
|
+
```
|
78
|
+
|
79
|
+
On Linux system the configuration for SSL certificate validation is present by default.
|
80
|
+
|
68
81
|
Depending on your system's configuration, you may need to run this command
|
69
82
|
with root/Administrator privileges.
|
70
83
|
|
@@ -126,7 +139,7 @@ Some usage examples follow:
|
|
126
139
|
$ knife google server list -Z us-central2-a
|
127
140
|
|
128
141
|
# Create a server
|
129
|
-
$ knife google server create www1 -m n1-standard-1 -I
|
142
|
+
$ knife google server create www1 -m n1-standard-1 -I debian-7-wheezy-v20130723 -Z us-central2-a -i ~/.ssh/id_rsa -x jdoe
|
130
143
|
|
131
144
|
# Delete a server (along with Chef node and API client via --purge)
|
132
145
|
$ knife google server delete www1 --purge -Z us-central2-a
|
@@ -203,12 +216,26 @@ and upcoming maintenance windows. The output should look similar to:
|
|
203
216
|
### knife google server create
|
204
217
|
|
205
218
|
Use this command to create a new Google Compute Engine server (a.k.a.
|
206
|
-
instance). You must specify a name, the machine type, the zone, and
|
207
|
-
image.
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
219
|
+
instance). You must specify a name, the machine type, the zone, and
|
220
|
+
the the image name. Images provided by Google follow this naming
|
221
|
+
convention:
|
222
|
+
|
223
|
+
```
|
224
|
+
debian-7-wheezy-vYYYYMMDD
|
225
|
+
debian-6-squeeze-vYYYYMMDD
|
226
|
+
centos-6-vYYYYMMDD
|
227
|
+
```
|
228
|
+
|
229
|
+
By default, the plugin will look for the specified image in the instance's
|
230
|
+
primary project first and then consult GCE's officially supported image
|
231
|
+
locations. The `-J IMAGE_PROJECT_ID` option can be specified to force the
|
232
|
+
plugin to look for the image in an alternate project location.
|
233
|
+
|
234
|
+
Note that if you are bootstrapping the node, make sure to follow the
|
235
|
+
preparation instructions earlier and use the `-x` and `-i` commands
|
236
|
+
to specify the username and the identity file for that user. Make sure
|
237
|
+
to use the private key file (e.g. `~/.ssh/id_rsa`) for the identity
|
238
|
+
file and *not* the public key file.
|
212
239
|
|
213
240
|
See the extended options that also allow bootstrapping the node with
|
214
241
|
`knife google server create --help`.
|
@@ -44,11 +44,16 @@ class Chef
|
|
44
44
|
:description => "The Image for the server",
|
45
45
|
:required => true
|
46
46
|
|
47
|
+
option :image_project_id,
|
48
|
+
:short => "-J IMAGE_PROJECT_ID",
|
49
|
+
:long => "--google-compute-image-project-id IMAGE_PROJECT_ID",
|
50
|
+
:description => "The project-id containing the Image (debian-cloud, centos-cloud, etc)",
|
51
|
+
:default => ""
|
52
|
+
|
47
53
|
option :zone,
|
48
54
|
:short => "-Z ZONE",
|
49
55
|
:long => "--google-compute-zone ZONE",
|
50
|
-
:description => "The Zone for this server"
|
51
|
-
:required => true
|
56
|
+
:description => "The Zone for this server"
|
52
57
|
|
53
58
|
option :network,
|
54
59
|
:short => "-n NETWORK",
|
@@ -265,23 +270,59 @@ class Chef
|
|
265
270
|
end
|
266
271
|
|
267
272
|
begin
|
268
|
-
zone = client.zones.get(config[:zone]).self_link
|
273
|
+
zone = client.zones.get(config[:zone] || Chef::Config[:knife][:google_compute_zone]).self_link
|
269
274
|
rescue Google::Compute::ResourceNotFound
|
270
|
-
ui.error("Zone '#{config[:zone]}' not found")
|
275
|
+
ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:google_compute_zone]}' not found")
|
276
|
+
exit 1
|
277
|
+
rescue Google::Compute::ParameterValidation
|
278
|
+
ui.error("Must specify zone in knife config file or in command line as an option. Try --help.")
|
271
279
|
exit 1
|
272
280
|
end
|
281
|
+
|
273
282
|
begin
|
274
|
-
machine_type = client.machine_types.get(config[:machine_type]).self_link
|
283
|
+
machine_type = client.machine_types.get(:name=>config[:machine_type], :zone=>selflink2name(zone)).self_link
|
275
284
|
rescue Google::Compute::ResourceNotFound
|
276
285
|
ui.error("MachineType '#{config[:machine_type]}' not found")
|
277
286
|
exit 1
|
278
287
|
end
|
288
|
+
|
289
|
+
(checked_custom, checked_all) = false
|
279
290
|
begin
|
280
|
-
|
291
|
+
image_project = config[:image_project_id]
|
292
|
+
machine_type=~Regexp.new('/projects/(.*?)/')
|
293
|
+
project = $1
|
294
|
+
if image_project.empty?
|
295
|
+
unless checked_custom
|
296
|
+
checked_custom = true
|
297
|
+
ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
|
298
|
+
image = client.images.get(:project=>project, :name=>config[:image]).self_link
|
299
|
+
else
|
300
|
+
case config[:image].downcase
|
301
|
+
when /debian/
|
302
|
+
project = 'debian-cloud'
|
303
|
+
ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
|
304
|
+
when /centos/
|
305
|
+
project = 'centos-cloud'
|
306
|
+
ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
|
307
|
+
end
|
308
|
+
checked_all = true
|
309
|
+
image = client.images.get(:project=>project, :name=>config[:image]).self_link
|
310
|
+
end
|
311
|
+
else
|
312
|
+
checked_all = true
|
313
|
+
project = image_project
|
314
|
+
image = client.images.get(:project=>project, :name=>config[:image]).self_link
|
315
|
+
end
|
316
|
+
ui.info("Found Image '#{config[:image]}' in Project '#{project}'")
|
281
317
|
rescue Google::Compute::ResourceNotFound
|
282
|
-
|
283
|
-
|
318
|
+
unless checked_all then
|
319
|
+
retry
|
320
|
+
else
|
321
|
+
ui.error("Image '#{config[:image]}' not found")
|
322
|
+
exit 1
|
323
|
+
end
|
284
324
|
end
|
325
|
+
|
285
326
|
begin
|
286
327
|
network = client.networks.get(config[:network]).self_link
|
287
328
|
rescue Google::Compute::ResourceNotFound
|
@@ -21,6 +21,7 @@ class Chef
|
|
21
21
|
include Knife::GoogleBase
|
22
22
|
|
23
23
|
deps do
|
24
|
+
require 'chef/api_client'
|
24
25
|
require 'google/compute'
|
25
26
|
end
|
26
27
|
|
@@ -31,8 +32,7 @@ class Chef
|
|
31
32
|
option :zone,
|
32
33
|
:short => "-Z ZONE",
|
33
34
|
:long => "--google-compute-zone ZONE",
|
34
|
-
:description => "The Zone for this server"
|
35
|
-
:required => true
|
35
|
+
:description => "The Zone for this server"
|
36
36
|
|
37
37
|
option :purge,
|
38
38
|
:short => "-P",
|
@@ -60,9 +60,12 @@ class Chef
|
|
60
60
|
|
61
61
|
def run
|
62
62
|
begin
|
63
|
-
zone = client.zones.get(config[:zone]).self_link
|
63
|
+
zone = client.zones.get(config[:zone] || Chef::Config[:knife][:google_compute_zone]).self_link
|
64
64
|
rescue Google::Compute::ResourceNotFound
|
65
|
-
ui.error("Zone '#{config[:zone]}' not found")
|
65
|
+
ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:google_compute_zone]}' not found")
|
66
|
+
exit 1
|
67
|
+
rescue Google::Compute::ParameterValidation
|
68
|
+
ui.error("Must specify zone in knife config file or in command line as an option. Try --help.")
|
66
69
|
exit 1
|
67
70
|
end
|
68
71
|
|
@@ -87,9 +90,8 @@ class Chef
|
|
87
90
|
ui.warn("Deleted server '#{selflink2name(zone)}:#{instance.name}'")
|
88
91
|
|
89
92
|
if config[:purge]
|
90
|
-
|
91
|
-
destroy_item(Chef::
|
92
|
-
destroy_item(Chef::ApiClient, thing_to_delete, "client")
|
93
|
+
destroy_item(Chef::Node, instance.name, "node")
|
94
|
+
destroy_item(Chef::ApiClient, instance.name, "client")
|
93
95
|
else
|
94
96
|
ui.warn("Corresponding node and client for the #{instance.name} server were not deleted and remain registered with the Chef Server")
|
95
97
|
end
|
@@ -25,28 +25,23 @@ class Chef
|
|
25
25
|
option :zone,
|
26
26
|
:short => "-Z ZONE",
|
27
27
|
:long => "--google-compute-zone ZONE",
|
28
|
-
:description => "The Zone for this server"
|
29
|
-
:required => true
|
28
|
+
:description => "The Zone for this server"
|
30
29
|
|
31
30
|
def run
|
32
31
|
$stdout.sync = true
|
33
|
-
|
32
|
+
|
34
33
|
begin
|
35
|
-
zone = client.zones.get(config[:zone])
|
34
|
+
zone = client.zones.get(config[:zone] || Chef::Config[:knife][:google_compute_zone])
|
36
35
|
rescue Google::Compute::ResourceNotFound
|
37
|
-
ui.error("Zone '#{config[:zone]}' not found")
|
36
|
+
ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:google_compute_zone] }' not found.")
|
37
|
+
exit 1
|
38
|
+
rescue Google::Compute::ParameterValidation
|
39
|
+
ui.error("Must specify zone in knife config file or in command line as an option. Try --help.")
|
38
40
|
exit 1
|
39
41
|
end
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
ui.color('Type', :bold),
|
44
|
-
ui.color('Image', :bold),
|
45
|
-
ui.color('Public IP', :bold),
|
46
|
-
ui.color('Private IP', :bold),
|
47
|
-
ui.color('Disks', :bold),
|
48
|
-
ui.color("Zone", :bold),
|
49
|
-
ui.color('Status', :bold)].flatten.compact
|
43
|
+
instance_label = ['Name', 'Type', 'Image', 'Public IP', 'Private IP', 'Disks', 'Zone', 'Status']
|
44
|
+
instance_list = (instance_label.map {|label| ui.color(label, :bold)}).flatten.compact
|
50
45
|
|
51
46
|
output_column_count = instance_list.length
|
52
47
|
|
@@ -70,7 +65,12 @@ class Chef
|
|
70
65
|
end
|
71
66
|
end
|
72
67
|
end
|
73
|
-
|
68
|
+
|
69
|
+
if instance_list.count > 8 # This condition checks if there are any servers available. The first 8 values are the Labels.
|
70
|
+
puts ui.list(instance_list, :uneven_columns_across, output_column_count)
|
71
|
+
else
|
72
|
+
puts "No servers found in #{zone.name} zone."
|
73
|
+
end
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
@@ -76,24 +76,32 @@ module Google
|
|
76
76
|
$stdout.print "\n\nAuthorization code: "
|
77
77
|
authorization_code = $stdin.gets.chomp
|
78
78
|
api_client.authorization.code = authorization_code
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
filepath = File.expand_path(DEFAULT_FILE)
|
79
|
+
|
80
|
+
begin
|
81
|
+
api_client.authorization.fetch_access_token!
|
82
|
+
rescue Faraday::Error::ConnectionFailed => e
|
83
|
+
raise ConnectionFail,
|
84
|
+
"The SSL certificates validation may not configured for this system. Please refer README to configured SSL certificates validation"\
|
85
|
+
if e.message.include? "SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed"
|
87
86
|
else
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
87
|
+
access_token = api_client.authorization.access_token
|
88
|
+
refresh_token = api_client.authorization.refresh_token
|
89
|
+
id_token = api_client.authorization.id_token
|
90
|
+
expires_in = api_client.authorization.expires_in
|
91
|
+
issued_at = api_client.authorization.issued_at.to_s
|
92
|
+
if !@credential_file
|
93
|
+
filepath = File.expand_path(DEFAULT_FILE)
|
94
|
+
else
|
95
|
+
filepath = File.expand_path(@credential_file)
|
96
|
+
end
|
97
|
+
File.open(filepath,'w+') do |f|
|
98
|
+
f.write(MultiJson.dump({"authorization_uri" => authorization_uri,
|
99
|
+
"token_credential_uri"=>"https://accounts.google.com/o/oauth2/token",
|
100
|
+
"scope"=>scope,"redirect_uri"=>redirect_uri, "client_id"=>client_id,
|
101
|
+
"client_secret"=>client_secret, "access_token"=>access_token,
|
102
|
+
"expires_in"=>expires_in,"refresh_token"=> refresh_token, "id_token"=>id_token,
|
103
|
+
"issued_at"=>issued_at,"project"=>project }, :pretty=>true))
|
104
|
+
end
|
97
105
|
end
|
98
106
|
end
|
99
107
|
|
@@ -154,7 +162,7 @@ module Google
|
|
154
162
|
end
|
155
163
|
|
156
164
|
def compute
|
157
|
-
@compute ||= @api_client.discovered_api('compute','
|
165
|
+
@compute ||= @api_client.discovered_api('compute','v1beta15')
|
158
166
|
end
|
159
167
|
|
160
168
|
def dispatch(opts)
|
@@ -33,6 +33,12 @@ module Google
|
|
33
33
|
def insert(options={})
|
34
34
|
create(options)
|
35
35
|
end
|
36
|
+
|
37
|
+
def create_snapshot(options={})
|
38
|
+
data = @dispatcher.dispatch(:api_method => api_resource.create_snapshot, :parameters=>options)
|
39
|
+
ZoneOperation.new(data.merge!(:dispatcher=>@dispatcher)) unless data.nil?
|
40
|
+
end
|
41
|
+
|
36
42
|
end
|
37
43
|
end
|
38
44
|
end
|
@@ -14,6 +14,7 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
+
require 'faraday'
|
17
18
|
module Google
|
18
19
|
module Compute
|
19
20
|
class ParameterValidation < ArgumentError; end
|
@@ -23,6 +24,7 @@ module Google
|
|
23
24
|
class OperationTimeout < RuntimeError; end
|
24
25
|
class HashConvert < RuntimeError; end
|
25
26
|
class ResourceNotFound < RuntimeError; end
|
27
|
+
class ConnectionFail < Faraday::Error::ConnectionFailed; end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
data/lib/knife-google/version.rb
CHANGED
@@ -26,7 +26,7 @@ describe Chef::Knife::GoogleBase do
|
|
26
26
|
|
27
27
|
it "#selflink2name should return name from a seleflink url" do
|
28
28
|
knife_plugin.selflink2name(
|
29
|
-
'https://www.googleapis.com/compute/
|
29
|
+
'https://www.googleapis.com/compute/v1beta15/projects/mock-project/category/resource').
|
30
30
|
should eq('resource')
|
31
31
|
end
|
32
32
|
|
@@ -16,24 +16,19 @@ require 'spec_helper'
|
|
16
16
|
|
17
17
|
describe Chef::Knife::GoogleServerCreate do
|
18
18
|
|
19
|
-
|
20
|
-
Chef::Knife::GoogleServerCreate.new(["-m"+stored_machine_type.name,
|
21
|
-
"-I"+stored_image.name, "-n"+stored_network.name,
|
22
|
-
"-Z"+stored_zone.name, stored_instance.name])
|
23
|
-
end
|
24
|
-
|
25
|
-
it "#run should invoke compute api to create an server" do
|
19
|
+
before(:each) do
|
26
20
|
zones = mock(Google::Compute::ListableResourceCollection)
|
27
21
|
zones.should_receive(:get).with(stored_zone.name).
|
28
22
|
and_return(stored_zone)
|
29
23
|
|
30
24
|
machine_types = mock(Google::Compute::ListableResourceCollection)
|
31
|
-
machine_types.should_receive(:get).
|
25
|
+
machine_types.should_receive(:get).
|
26
|
+
with({:name=>stored_machine_type.name, :zone=>stored_zone.name}).
|
32
27
|
and_return(stored_machine_type)
|
33
28
|
|
34
29
|
images = mock(Google::Compute::ListableResourceCollection)
|
35
30
|
images.should_receive(:get).
|
36
|
-
with({:project=>"
|
31
|
+
with({:project=>"debian-cloud", :name=>stored_image.name}).
|
37
32
|
and_return(stored_image)
|
38
33
|
|
39
34
|
networks = mock(Google::Compute::ListableResourceCollection)
|
@@ -59,7 +54,13 @@ describe Chef::Knife::GoogleServerCreate do
|
|
59
54
|
:images=>images, :zones=>zones,:machine_types=>machine_types,
|
60
55
|
:networks=>networks)
|
61
56
|
Google::Compute::Client.stub!(:from_json).and_return(client)
|
57
|
+
end
|
62
58
|
|
59
|
+
it "#run should invoke compute api to create an server" do
|
60
|
+
knife_plugin = Chef::Knife::GoogleServerCreate.new(["-m"+stored_machine_type.name,
|
61
|
+
"-I"+stored_image.name, "-J"+"debian-cloud",
|
62
|
+
"-n"+stored_network.name,
|
63
|
+
"-Z"+stored_zone.name, stored_instance.name])
|
63
64
|
knife_plugin.config[:disks]=[]
|
64
65
|
knife_plugin.config[:metadata]=[]
|
65
66
|
knife_plugin.config[:public_ip]='EPHEMERAL'
|
@@ -73,12 +74,31 @@ describe Chef::Knife::GoogleServerCreate do
|
|
73
74
|
knife_plugin.run
|
74
75
|
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
77
|
+
it "should read zone value from knife config file." do
|
78
|
+
Chef::Config[:knife][:google_compute_zone] = stored_zone.name
|
79
|
+
knife_plugin = Chef::Knife::GoogleServerCreate.new(["-m"+stored_machine_type.name,
|
80
|
+
"-I"+stored_image.name, "-J"+"debian-cloud",
|
81
|
+
"-n"+stored_network.name,
|
82
|
+
stored_instance.name])
|
83
|
+
knife_plugin.config[:disks]=[]
|
84
|
+
knife_plugin.config[:metadata]=[]
|
85
|
+
knife_plugin.config[:public_ip]='EPHEMERAL'
|
86
|
+
knife_plugin.ui.stub!(:info)
|
87
|
+
|
88
|
+
knife_plugin.stub!(:wait_for_sshd)
|
89
|
+
knife_plugin.should_receive(:bootstrap_for_node).
|
90
|
+
with(stored_instance,'10.100.0.10').
|
91
|
+
and_return(mock("Chef::Knife::Bootstrap",:run=>true))
|
92
|
+
knife_plugin.run
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "without appropriate command line options" do
|
98
|
+
it "should throw exception when required params are not passed" do
|
99
|
+
$stdout.stub!(:write) # lets not print those error messages
|
100
|
+
expect {
|
101
|
+
Chef::Knife::GoogleServerCreate.new([ "NAME"])
|
102
|
+
}.to raise_error(SystemExit)
|
83
103
|
end
|
84
104
|
end
|
@@ -103,3 +103,25 @@ describe Chef::Knife::GoogleServerDelete do
|
|
103
103
|
end
|
104
104
|
end
|
105
105
|
end
|
106
|
+
|
107
|
+
describe Chef::Knife::GoogleServerDelete do
|
108
|
+
it "should read zone value from knife config file." do
|
109
|
+
Chef::Config[:knife][:google_compute_zone] = stored_zone.name
|
110
|
+
knife_plugin = Chef::Knife::GoogleServerDelete.new([stored_instance.name])
|
111
|
+
zones = mock(Google::Compute::ListableResourceCollection)
|
112
|
+
zones.should_receive(:get).with(stored_zone.name).and_return(stored_zone)
|
113
|
+
|
114
|
+
instances = mock(Google::Compute::DeletableResourceCollection)
|
115
|
+
instances.should_receive(:get).with(:name=>stored_instance.name, :zone=>stored_zone.name).
|
116
|
+
and_return(stored_instance)
|
117
|
+
instances.should_receive(:delete).with(:instance=>stored_instance.name, :zone=>stored_zone.name)
|
118
|
+
|
119
|
+
client = mock(Google::Compute::Client, :zones=>zones, :instances=>instances)
|
120
|
+
Google::Compute::Client.stub!(:from_json).and_return(client)
|
121
|
+
knife_plugin.config[:yes] = true
|
122
|
+
knife_plugin.ui.should_receive(:warn).twice
|
123
|
+
knife_plugin.stub!(:msg_pair)
|
124
|
+
knife_plugin.run
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|