knife-google 1.1.0 → 1.2.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.
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"