knife-google 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +155 -66
  3. data/knife-google.gemspec +2 -1
  4. data/lib/chef/knife/google_base.rb +1 -1
  5. data/lib/chef/knife/google_disk_create.rb +17 -9
  6. data/lib/chef/knife/google_disk_delete.rb +2 -2
  7. data/lib/chef/knife/google_disk_list.rb +8 -8
  8. data/lib/chef/knife/google_project_list.rb +178 -0
  9. data/lib/chef/knife/google_region_list.rb +125 -0
  10. data/lib/chef/knife/google_server_create.rb +174 -46
  11. data/lib/chef/knife/google_server_delete.rb +9 -13
  12. data/lib/chef/knife/google_server_list.rb +5 -6
  13. data/lib/chef/knife/google_zone_list.rb +22 -28
  14. data/lib/google/compute.rb +2 -1
  15. data/lib/google/compute/client.rb +12 -7
  16. data/lib/google/compute/creatable_resource_collection.rb +12 -1
  17. data/lib/google/compute/disk.rb +0 -2
  18. data/lib/google/compute/image.rb +1 -2
  19. data/lib/google/compute/project.rb +2 -2
  20. data/lib/google/compute/{kernel.rb → region.rb} +12 -1
  21. data/lib/google/compute/region_operation.rb +62 -0
  22. data/lib/google/compute/server.rb +3 -2
  23. data/lib/google/compute/server/attached_disk.rb +3 -3
  24. data/lib/google/compute/zone.rb +6 -4
  25. data/lib/google/compute/zone_operation.rb +11 -9
  26. data/lib/knife-google/version.rb +1 -1
  27. data/spec/chef/knife/google_base_spec.rb +4 -4
  28. data/spec/chef/knife/google_disk_create_spec.rb +8 -7
  29. data/spec/chef/knife/google_disk_delete_spec.rb +15 -16
  30. data/spec/chef/knife/google_disk_list_spec.rb +6 -6
  31. data/spec/chef/knife/google_region_list_spec.rb +32 -0
  32. data/spec/chef/knife/google_server_create_spec.rb +78 -44
  33. data/spec/chef/knife/google_server_delete_spec.rb +37 -37
  34. data/spec/chef/knife/google_server_list_spec.rb +8 -7
  35. data/spec/chef/knife/google_setup_spec.rb +1 -2
  36. data/spec/chef/knife/google_zone_list_spec.rb +4 -4
  37. data/spec/data/{compute-v1beta15.json → compute-v1.json} +700 -246
  38. data/spec/data/disk.json +3 -4
  39. data/spec/data/firewall.json +2 -2
  40. data/spec/data/global_operation.json +3 -3
  41. data/spec/data/image.json +2 -2
  42. data/spec/data/machine_type.json +1 -1
  43. data/spec/data/network.json +1 -1
  44. data/spec/data/project.json +1 -1
  45. data/spec/data/region.json +23 -0
  46. data/spec/data/serial_port_output.json +1 -1
  47. data/spec/data/server.json +7 -7
  48. data/spec/data/snapshot.json +2 -2
  49. data/spec/data/zone.json +7 -15
  50. data/spec/data/zone_operation.json +3 -3
  51. data/spec/google/compute/disk_spec.rb +32 -32
  52. data/spec/google/compute/firewall_spec.rb +37 -37
  53. data/spec/google/compute/global_operation_spec.rb +9 -9
  54. data/spec/google/compute/image_spec.rb +17 -17
  55. data/spec/google/compute/machine_type_spec.rb +7 -7
  56. data/spec/google/compute/network_spec.rb +14 -14
  57. data/spec/google/compute/project_spec.rb +17 -17
  58. data/spec/google/compute/region_spec.rb +51 -0
  59. data/spec/google/compute/server_spec.rb +35 -42
  60. data/spec/google/compute/snapshot_spec.rb +11 -11
  61. data/spec/google/compute/zone_operation_spec.rb +9 -9
  62. data/spec/google/compute/zone_spec.rb +7 -7
  63. data/spec/spec_helper.rb +1 -0
  64. data/spec/support/mocks.rb +10 -10
  65. data/spec/support/resource_examples.rb +7 -7
  66. data/spec/support/spec_google_base.rb +4 -0
  67. metadata +14 -26
  68. data/spec/data/kernel.json +0 -15
  69. data/spec/google/compute/kernel_spec.rb +0 -49
@@ -0,0 +1,178 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ require 'chef/knife/google_base'
16
+ require 'time'
17
+
18
+ class Chef
19
+ class Knife
20
+ class GoogleProjectList < Knife
21
+
22
+ include Knife::GoogleBase
23
+
24
+ banner "knife google project list (options)"
25
+
26
+ option :limits,
27
+ :short => "-L",
28
+ :long => "--with-limits",
29
+ :description => "Additionally print the quota limit for each metric",
30
+ :required => false,
31
+ :boolean => true,
32
+ :default => false
33
+
34
+ def run
35
+ $stdout.sync = true
36
+
37
+ project_list = [
38
+ ui.color("name", :bold),
39
+ ui.color('snapshots', :bold),
40
+ ui.color('networks', :bold),
41
+ ui.color('firewalls', :bold),
42
+ ui.color('images', :bold),
43
+ ui.color('routes', :bold),
44
+ ui.color('forwarding-rules', :bold),
45
+ ui.color('target-pools', :bold),
46
+ ui.color('health-checks', :bold)].flatten.compact
47
+
48
+ output_column_count = project_list.length
49
+
50
+ project = client.projects.project
51
+
52
+ project_list << project
53
+
54
+ snapshots_usage = "0"
55
+ snapshots_limit = "0"
56
+ client.projects.get(project).quotas.each do |quota|
57
+ if quota["metric"] == "SNAPSHOTS"
58
+ snapshots_usage = "#{quota["usage"].to_i}"
59
+ snapshots_limit = "#{quota["limit"].to_i}"
60
+ end
61
+ end
62
+ if config[:limits] == true
63
+ snapshots_quota = "#{snapshots_usage}/#{snapshots_limit}"
64
+ else
65
+ snapshots_quota = "#{snapshots_usage}"
66
+ end
67
+ project_list << snapshots_quota
68
+
69
+ networks_usage = "0"
70
+ networks_limit = "0"
71
+ client.projects.get(project).quotas.each do |quota|
72
+ if quota["metric"] == "NETWORKS"
73
+ networks_usage = "#{quota["usage"].to_i}"
74
+ networks_limit = "#{quota["limit"].to_i}"
75
+ end
76
+ end
77
+ if config[:limits] == true
78
+ networks_quota = "#{networks_usage}/#{networks_limit}"
79
+ else
80
+ networks_quota = "#{networks_usage}"
81
+ end
82
+ project_list << networks_quota
83
+
84
+ firewalls_usage = "0"
85
+ firewalls_limit = "0"
86
+ client.projects.get(project).quotas.each do |quota|
87
+ if quota["metric"] == "FIREWALLS"
88
+ firewalls_usage = "#{quota["usage"].to_i}"
89
+ firewalls_limit = "#{quota["limit"].to_i}"
90
+ end
91
+ end
92
+ if config[:limits] == true
93
+ firewalls_quota = "#{firewalls_usage}/#{firewalls_limit}"
94
+ else
95
+ firewalls_quota = "#{firewalls_usage}"
96
+ end
97
+ project_list << firewalls_quota
98
+
99
+ images_usage = "0"
100
+ images_limit = "0"
101
+ client.projects.get(project).quotas.each do |quota|
102
+ if quota["metric"] == "IMAGES"
103
+ images_usage = "#{quota["usage"].to_i}"
104
+ images_limit = "#{quota["limit"].to_i}"
105
+ end
106
+ end
107
+ if config[:limits] == true
108
+ images_quota = "#{images_usage}/#{images_limit}"
109
+ else
110
+ images_quota = "#{images_usage}"
111
+ end
112
+ project_list << images_quota
113
+
114
+ routes_usage = "0"
115
+ routes_limit = "0"
116
+ client.projects.get(project).quotas.each do |quota|
117
+ if quota["metric"] == "ROUTES"
118
+ routes_usage = "#{quota["usage"].to_i}"
119
+ routes_limit = "#{quota["limit"].to_i}"
120
+ end
121
+ end
122
+ if config[:limits] == true
123
+ routes_quota = "#{routes_usage}/#{routes_limit}"
124
+ else
125
+ routes_quota = "#{routes_usage}"
126
+ end
127
+ project_list << routes_quota
128
+
129
+ forwarding_usage = "0"
130
+ forwarding_limit = "0"
131
+ client.projects.get(project).quotas.each do |quota|
132
+ if quota["metric"] == "FORWARDING_RULES"
133
+ forwarding_usage = "#{quota["usage"].to_i}"
134
+ forwarding_limit = "#{quota["limit"].to_i}"
135
+ end
136
+ end
137
+ if config[:limits] == true
138
+ forwarding_quota = "#{forwarding_usage}/#{forwarding_limit}"
139
+ else
140
+ forwarding_quota = "#{forwarding_usage}"
141
+ end
142
+ project_list << forwarding_quota
143
+
144
+ target_usage = "0"
145
+ target_limit = "0"
146
+ client.projects.get(project).quotas.each do |quota|
147
+ if quota["metric"] == "TARGET_POOLS"
148
+ target_usage = "#{quota["usage"].to_i}"
149
+ target_limit = "#{quota["limit"].to_i}"
150
+ end
151
+ end
152
+ if config[:limits] == true
153
+ target_quota = "#{target_usage}/#{target_limit}"
154
+ else
155
+ target_quota = "#{target_usage}"
156
+ end
157
+ project_list << target_quota
158
+
159
+ health_usage = "0"
160
+ health_limit = "0"
161
+ client.projects.get(project).quotas.each do |quota|
162
+ if quota["metric"] == "HEALTH_CHECKS"
163
+ health_usage = "#{quota["usage"].to_i}"
164
+ health_limit = "#{quota["limit"].to_i}"
165
+ end
166
+ end
167
+ if config[:limits] == true
168
+ health_quota = "#{health_usage}/#{health_limit}"
169
+ else
170
+ health_quota = "#{health_usage}"
171
+ end
172
+ project_list << health_quota
173
+
174
+ ui.info(ui.list(project_list, :uneven_columns_across, output_column_count))
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,125 @@
1
+ # Copyright 2013 Google Inc. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ require 'chef/knife/google_base'
16
+ require 'time'
17
+
18
+ class Chef
19
+ class Knife
20
+ class GoogleRegionList < Knife
21
+
22
+ include Knife::GoogleBase
23
+
24
+ banner "knife google region list (options)"
25
+
26
+ option :limits,
27
+ :short => "-L",
28
+ :long => "--with-limits",
29
+ :description => "Additionally print the quota limit for each metric",
30
+ :required => false,
31
+ :boolean => true,
32
+ :default => false
33
+
34
+ def run
35
+ $stdout.sync = true
36
+
37
+ region_list = [
38
+ ui.color("name", :bold),
39
+ ui.color('status', :bold),
40
+ ui.color('deprecation', :bold),
41
+ ui.color('cpus', :bold),
42
+ ui.color('disks-total-gb', :bold),
43
+ ui.color('in-use-addresses', :bold),
44
+ ui.color('static-addresses', :bold)].flatten.compact
45
+
46
+ output_column_count = region_list.length
47
+
48
+ client.regions.list.each do |region|
49
+ region_list << region.name
50
+ region_list << begin
51
+ status = region.status.downcase
52
+ case status
53
+ when 'up'
54
+ ui.color(status, :green)
55
+ else
56
+ ui.color(status, :red)
57
+ end
58
+ end
59
+ deprecation_state = "-"
60
+ if region.deprecated.respond_to?('state')
61
+ deprecation_state = region.deprecated.state
62
+ end
63
+ region_list << deprecation_state
64
+ cpu_usage = "0"
65
+ cpu_limit = "0"
66
+ region.quotas.each do |quota|
67
+ if quota["metric"] == "CPUS"
68
+ cpu_usage = "#{quota["usage"].to_i}"
69
+ cpu_limit = "#{quota["limit"].to_i}"
70
+ end
71
+ end
72
+ if config[:limits] == true
73
+ cpu_quota = "#{cpu_usage}/#{cpu_limit}"
74
+ else
75
+ cpu_quota = "#{cpu_usage}"
76
+ end
77
+ region_list << cpu_quota
78
+ disk_usage = "0"
79
+ disk_limit = "0"
80
+ region.quotas.each do |quota|
81
+ if quota["metric"] == "DISKS_TOTAL_GB"
82
+ disk_usage = "#{quota["usage"].to_i}"
83
+ disk_limit = "#{quota["limit"].to_i}"
84
+ end
85
+ end
86
+ if config[:limits] == true
87
+ disk_quota = "#{disk_usage}/#{disk_limit}"
88
+ else
89
+ disk_quota = "#{disk_usage}"
90
+ end
91
+ region_list << disk_quota
92
+ inuse_usage = "0"
93
+ inuse_limit = "0"
94
+ region.quotas.each do |quota|
95
+ if quota["metric"] == "IN_USE_ADDRESSES"
96
+ inuse_usage = "#{quota["usage"].to_i}"
97
+ inuse_limit = "#{quota["limit"].to_i}"
98
+ end
99
+ end
100
+ if config[:limits] == true
101
+ inuse_quota = "#{inuse_usage}/#{inuse_limit}"
102
+ else
103
+ inuse_quota = "#{inuse_usage}"
104
+ end
105
+ region_list << inuse_quota
106
+ static_usage = "0"
107
+ static_limit = "0"
108
+ region.quotas.each do |quota|
109
+ if quota["metric"] == "STATIC_ADDRESSES"
110
+ static_usage = "#{quota["usage"].to_i}"
111
+ static_limit = "#{quota["limit"].to_i}"
112
+ end
113
+ end
114
+ if config[:limits] == true
115
+ static_quota = "#{static_usage}/#{static_limit}"
116
+ else
117
+ static_quota = "#{static_usage}"
118
+ end
119
+ region_list << static_quota
120
+ end
121
+ ui.info(ui.list(region_list, :uneven_columns_across, output_column_count))
122
+ end
123
+ end
124
+ end
125
+ end
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  #
15
+ require 'timeout'
15
16
  require 'chef/knife/google_base'
16
17
 
17
18
  class Chef
@@ -34,47 +35,89 @@ class Chef
34
35
 
35
36
  option :machine_type,
36
37
  :short => "-m MACHINE_TYPE",
37
- :long => "--google-compute-machine MACHINE_TYPE",
38
+ :long => "--gce-machine-type MACHINE_TYPE",
38
39
  :description => "The machine type of server (n1-highcpu-2, n1-highcpu-2-d, etc)",
39
40
  :required => true
40
41
 
41
42
  option :image,
42
43
  :short => "-I IMAGE",
43
- :long => "--google-compute-image IMAGE",
44
+ :long => "--gce-image IMAGE",
44
45
  :description => "The Image for the server",
45
46
  :required => true
46
47
 
47
48
  option :image_project_id,
48
- :short => "-J IMAGE_PROJECT_ID",
49
- :long => "--google-compute-image-project-id IMAGE_PROJECT_ID",
49
+ :long => "--gce-image-project-id IMAGE_PROJECT_ID",
50
50
  :description => "The project-id containing the Image (debian-cloud, centos-cloud, etc)",
51
51
  :default => ""
52
52
 
53
53
  option :zone,
54
54
  :short => "-Z ZONE",
55
- :long => "--google-compute-zone ZONE",
55
+ :long => "--gce-zone ZONE",
56
56
  :description => "The Zone for this server"
57
57
 
58
+ option :boot_disk_name,
59
+ :long => "--gce-boot-disk-name DISK",
60
+ :description => "Name of persistent boot disk; default is to use the server name",
61
+ :default => ""
62
+
63
+ option :boot_disk_size,
64
+ :long => "--gce-boot-disk-size SIZE",
65
+ :description => "Size of the persistent boot disk between 10 and 10000 GB, specified in GB; default is '10' GB",
66
+ :default => "10"
67
+
68
+ option :auto_restart,
69
+ :long => "--[no-]gce-auto-server-restart",
70
+ :description => "Compute Engine can automatically restart your VM instance if it is terminated for non-user-initiated reasons; enabled by default.",
71
+ :boolean => true,
72
+ :default => true
73
+
74
+ option :auto_migrate,
75
+ :long => "--[no-]gce-auto-server-migrate",
76
+ :description => "Compute Engine can migrate your VM instance to other hardware without downtime prior to periodic infrastructure maintenance, otherwise the server is terminated; enabled by default.",
77
+ :boolean => true,
78
+ :default => true
79
+
58
80
  option :network,
59
81
  :short => "-n NETWORK",
60
- :long => "--google-compute-network NETWORK",
82
+ :long => "--gce-network NETWORK",
61
83
  :description => "The network for this server; default is 'default'",
62
84
  :default => "default"
63
85
 
64
86
  option :tags,
65
87
  :short => "-T TAG1,TAG2,TAG3",
66
- :long => "--google-compute-tags TAG1,TAG2,TAG3",
88
+ :long => "--gce-tags TAG1,TAG2,TAG3",
67
89
  :description => "Tags for this server",
68
90
  :proc => Proc.new { |tags| tags.split(',') },
69
91
  :default => []
70
92
 
71
93
  option :metadata,
72
- :short => "-M K=V[,K=V,...]",
73
- :long => "--google-compute-metadata Key=Value[,Key=Value...]",
74
- :description => "The metadata for this server",
94
+ :long => "--gce-metadata Key=Value[,Key=Value...]",
95
+ :description => "Additional metadata for this server",
75
96
  :proc => Proc.new { |metadata| metadata.split(',') },
76
97
  :default => []
77
98
 
99
+ option :service_account_scopes,
100
+ :long => "--gce-service-account-scopes SCOPE1,SCOPE2,SCOPE3",
101
+ :proc => Proc.new { |service_account_scopes| service_account_scopes.split(',') },
102
+ :description => "Service account scopes for this server",
103
+ :default => []
104
+
105
+ # GCE documentation uses the term 'service account name', the api uses the term 'email'
106
+ option :service_account_name,
107
+ :long => "--gce-service-account-name NAME",
108
+ :description => "Service account name for this server, typically in the form of '123845678986@project.gserviceaccount.com'; default is 'default'",
109
+ :default => "default"
110
+
111
+ option :instance_connect_ip,
112
+ :long => "--gce-server-connect-ip INTERFACE",
113
+ :description => "Whether to use PUBLIC or PRIVATE interface/address to connect; default is 'PUBLIC'",
114
+ :default => 'PUBLIC'
115
+
116
+ option :public_ip,
117
+ :long=> "--gce-public-ip IP_ADDRESS",
118
+ :description => "EPHEMERAL or static IP address or NONE; default is 'EPHEMERAL'",
119
+ :default => "EPHEMERAL"
120
+
78
121
  option :chef_node_name,
79
122
  :short => "-N NAME",
80
123
  :long => "--node-name NAME",
@@ -158,23 +201,6 @@ class Chef
158
201
  Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new
159
202
  }
160
203
 
161
- option :instance_connect_ip,
162
- :long => "--google-compute-server-connect-ip PUBLIC",
163
- :short => "-a PUBLIC",
164
- :description => "Whether to use PUBLIC or PRIVATE address to connect; default is 'PUBLIC'",
165
- :default => 'PUBLIC'
166
-
167
- option :disks,
168
- :long=> "--google-compute-disks DISK1,DISK2",
169
- :proc => Proc.new { |metadata| metadata.split(',') },
170
- :description => "Disks to be attached",
171
- :default => []
172
-
173
- option :public_ip,
174
- :long=> "--google-compute-public-ip IP_ADDRESS",
175
- :description => "EPHEMERAL or static IP address or NONE; default is 'EPHEMERAL'",
176
- :default => "EPHEMERAL"
177
-
178
204
  def tcp_test_ssh(hostname, ssh_port)
179
205
  tcp_socket = TCPSocket.new(hostname, ssh_port)
180
206
  readable = IO.select([tcp_socket], nil, nil, 5)
@@ -200,7 +226,7 @@ class Chef
200
226
 
201
227
  def wait_for_tunnelled_sshd(hostname)
202
228
  print(".")
203
- print(".") until tunnel_test_ssh(ssh_connect_host) {
229
+ print(".") until tunnel_test_ssh(hostname) {
204
230
  sleep @initial_sleep_delay ||= 40
205
231
  puts("done")
206
232
  }
@@ -237,7 +263,35 @@ class Chef
237
263
  end
238
264
  end
239
265
 
240
- def bootstrap_for_node(instance,ssh_host)
266
+ def disk_exists(disk, zone)
267
+ # if client.disks.get errors with a Google::Compute::ResourceNotFound
268
+ # then the disk does not exist and can be created
269
+ client.disks.get(:disk => disk, :zone => selflink2name(zone))
270
+ rescue Google::Compute::ResourceNotFound
271
+ # disk does not exist
272
+ # continue provisioning
273
+ false
274
+ else
275
+ true
276
+ end
277
+
278
+ def wait_for_disk(disk, operation, zone)
279
+ Timeout::timeout(300) do
280
+ until disk.status == 'DONE'
281
+ ui.info(".")
282
+ sleep 1
283
+ disk = client.zoneOperations.get(:name => disk,
284
+ :operation => operation,
285
+ :zone => selflink2name(zone))
286
+ end
287
+ disk.target_link
288
+ end
289
+ rescue Timeout::Error
290
+ ui.error("Timeout exceeded with disk status: " + disk.status)
291
+ exit 1
292
+ end
293
+
294
+ def bootstrap_for_node(instance, ssh_host)
241
295
  bootstrap = Chef::Knife::Bootstrap.new
242
296
  bootstrap.name_args = [ssh_host]
243
297
  bootstrap.config[:run_list] = config[:run_list]
@@ -258,6 +312,7 @@ class Chef
258
312
  # Modify global configuration state to ensure hint gets set by
259
313
  # knife-bootstrap
260
314
  Chef::Config[:knife][:hints] ||= {}
315
+ Chef::Config[:knife][:hints]["gce"] ||= {}
261
316
  Chef::Config[:knife][:hints]["google"] ||= {}
262
317
  bootstrap
263
318
  end
@@ -265,14 +320,14 @@ class Chef
265
320
  def run
266
321
  $stdout.sync = true
267
322
  unless @name_args.size > 0
268
- ui.error("Please provide the name of the new server")
323
+ ui.error("Please provide the name of the new server.")
269
324
  exit 1
270
325
  end
271
326
 
272
327
  begin
273
- zone = client.zones.get(config[:zone] || Chef::Config[:knife][:google_compute_zone]).self_link
328
+ zone = client.zones.get(config[:zone] || Chef::Config[:knife][:gce_zone]).self_link
274
329
  rescue Google::Compute::ResourceNotFound
275
- ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:google_compute_zone]}' not found")
330
+ ui.error("Zone '#{config[:zone] || Chef::Config[:knife][:gce_zone]}' not found.")
276
331
  exit 1
277
332
  rescue Google::Compute::ParameterValidation
278
333
  ui.error("Must specify zone in knife config file or in command line as an option. Try --help.")
@@ -280,18 +335,33 @@ class Chef
280
335
  end
281
336
 
282
337
  begin
283
- machine_type = client.machine_types.get(:name=>config[:machine_type], :zone=>selflink2name(zone)).self_link
338
+ machine_type = client.machine_types.get(:name => config[:machine_type],
339
+ :zone => selflink2name(zone)).self_link
284
340
  rescue Google::Compute::ResourceNotFound
285
341
  ui.error("MachineType '#{config[:machine_type]}' not found")
286
342
  exit 1
287
343
  end
288
344
 
345
+ # this parameter is a string during the post and boolean otherwise
346
+ if config[:auto_restart] then
347
+ auto_restart = 'true'
348
+ else
349
+ auto_restart = 'false'
350
+ end
351
+
352
+ if config[:auto_migrate] then
353
+ auto_migrate = 'MIGRATE'
354
+ else
355
+ auto_migrate = 'TERMINATE'
356
+ end
357
+
289
358
  (checked_custom, checked_all) = false
290
359
  begin
291
360
  image_project = config[:image_project_id]
292
- machine_type=~Regexp.new('/projects/(.*?)/')
361
+ # use zone url to determine project name
362
+ zone =~ Regexp.new('/projects/(.*?)/')
293
363
  project = $1
294
- if image_project.empty?
364
+ if image_project.to_s.empty?
295
365
  unless checked_custom
296
366
  checked_custom = true
297
367
  ui.info("Looking for Image '#{config[:image]}' in Project '#{project}'")
@@ -323,6 +393,27 @@ class Chef
323
393
  end
324
394
  end
325
395
 
396
+ begin
397
+ boot_disk_size = config[:boot_disk_size].to_i
398
+ raise if !boot_disk_size.between?(10, 10000)
399
+ rescue
400
+ ui.error("Size of the persistent boot disk must be between 10 and 10000 GB.")
401
+ exit 1
402
+ end
403
+
404
+ if config[:boot_disk_name].to_s.empty? then
405
+ boot_disk_name = @name_args.first
406
+ else
407
+ boot_disk_name = config[:boot_disk_name]
408
+ end
409
+
410
+ ui.info("Waiting for the disk insert operation to complete")
411
+ boot_disk_insert = client.disks.insert(:sourceImage => image,
412
+ :zone => selflink2name(zone),
413
+ :name => boot_disk_name,
414
+ :sizeGb => boot_disk_size)
415
+ boot_disk_target_link = wait_for_disk(boot_disk_insert, boot_disk_insert.name, zone)
416
+
326
417
  begin
327
418
  network = client.networks.get(config[:network]).self_link
328
419
  rescue Google::Compute::ResourceNotFound
@@ -330,7 +421,6 @@ class Chef
330
421
  exit 1
331
422
  end
332
423
 
333
- disks = config[:disks].collect{|disk| client.disks.get(:disk=>disk, :zone=>selflink2name(zone)).self_link}
334
424
  metadata = config[:metadata].collect{|pair| Hash[*pair.split('=')] }
335
425
  network_interface = {'network'=>network}
336
426
 
@@ -346,27 +436,65 @@ class Chef
346
436
  ui.error("Invalid public ip value : #{config[:public_ip]}")
347
437
  exit 1
348
438
  end
349
- zone_operation = client.instances.create(:name=>@name_args.first, :zone=>selflink2name(zone),
350
- :image=> image,
351
- :machineType =>machine_type,
352
- :disks=>disks,
353
- :metadata=>{'items'=> metadata },
354
- :networkInterfaces => [network_interface],
355
- :tags=> config[:tags]
356
- )
357
439
 
358
440
  ui.info("Waiting for the create server operation to complete")
441
+ if !config[:service_account_scopes].any?
442
+ zone_operation = client.instances.create(:name => @name_args.first,
443
+ :zone => selflink2name(zone),
444
+ :machineType => machine_type,
445
+ :disks => [{
446
+ 'boot' => true,
447
+ 'type' => 'PERSISTENT',
448
+ 'mode' => 'READ_WRITE',
449
+ 'deviceName' => selflink2name(boot_disk_target_link),
450
+ 'source' => boot_disk_target_link
451
+ }],
452
+ :networkInterfaces => [network_interface],
453
+ :scheduling => {
454
+ 'automaticRestart' => auto_restart,
455
+ 'onHostMaintenance' => auto_migrate
456
+ },
457
+ :metadata => { 'items' => metadata },
458
+ :tags => { 'items' => config[:tags] }
459
+ )
460
+ else
461
+ zone_operation = client.instances.create(:name => @name_args.first,
462
+ :zone=> selflink2name(zone),
463
+ :machineType => machine_type,
464
+ :disks => [{
465
+ 'boot' => true,
466
+ 'type' => 'PERSISTENT',
467
+ 'mode' => 'READ_WRITE',
468
+ 'deviceName' => selflink2name(boot_disk_target_link),
469
+ 'source' => boot_disk_target_link
470
+ }],
471
+ :networkInterfaces => [network_interface],
472
+ :serviceAccounts => [{
473
+ 'kind' => 'compute#serviceAccount',
474
+ 'email' => config[:service_account_name],
475
+ 'scopes' => config[:service_account_scopes]
476
+ }],
477
+ :scheduling => {
478
+ 'automaticRestart' => auto_restart,
479
+ 'onHostMaintenance' => auto_migrate
480
+ },
481
+ :metadata => { 'items'=>metadata },
482
+ :tags => { 'items' => config[:tags] }
483
+ )
484
+ end
485
+
359
486
  until zone_operation.progress.to_i == 100
360
487
  ui.info(".")
361
488
  sleep 1
362
489
  zone_operation = client.zoneOperations.get(:name=>zone_operation, :operation=>zone_operation.name, :zone=>selflink2name(zone))
363
490
  end
491
+
364
492
  ui.info("Waiting for the servers to be in running state")
365
493
 
366
494
  @instance = client.instances.get(:name=>@name_args.first, :zone=>selflink2name(zone))
367
495
  msg_pair("Instance Name", @instance.name)
368
- msg_pair("MachineType", selflink2name(@instance.machine_type))
369
- msg_pair("Image", selflink2name(@instance.image))
496
+ msg_pair("Machine Type", selflink2name(@instance.machine_type))
497
+ msg_pair("Image", selflink2name(config[:image]))
370
498
  msg_pair("Zone", selflink2name(@instance.zone))
371
499
  msg_pair("Tags", @instance.tags.has_key?("items") ? @instance.tags["items"].join(",") : "None")
372
500
  until @instance.status == "RUNNING"