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.
- checksums.yaml +15 -0
- data/README.md +155 -66
- data/knife-google.gemspec +2 -1
- data/lib/chef/knife/google_base.rb +1 -1
- data/lib/chef/knife/google_disk_create.rb +17 -9
- data/lib/chef/knife/google_disk_delete.rb +2 -2
- data/lib/chef/knife/google_disk_list.rb +8 -8
- data/lib/chef/knife/google_project_list.rb +178 -0
- data/lib/chef/knife/google_region_list.rb +125 -0
- data/lib/chef/knife/google_server_create.rb +174 -46
- data/lib/chef/knife/google_server_delete.rb +9 -13
- data/lib/chef/knife/google_server_list.rb +5 -6
- data/lib/chef/knife/google_zone_list.rb +22 -28
- data/lib/google/compute.rb +2 -1
- data/lib/google/compute/client.rb +12 -7
- data/lib/google/compute/creatable_resource_collection.rb +12 -1
- data/lib/google/compute/disk.rb +0 -2
- data/lib/google/compute/image.rb +1 -2
- data/lib/google/compute/project.rb +2 -2
- data/lib/google/compute/{kernel.rb → region.rb} +12 -1
- data/lib/google/compute/region_operation.rb +62 -0
- data/lib/google/compute/server.rb +3 -2
- data/lib/google/compute/server/attached_disk.rb +3 -3
- data/lib/google/compute/zone.rb +6 -4
- data/lib/google/compute/zone_operation.rb +11 -9
- data/lib/knife-google/version.rb +1 -1
- data/spec/chef/knife/google_base_spec.rb +4 -4
- data/spec/chef/knife/google_disk_create_spec.rb +8 -7
- data/spec/chef/knife/google_disk_delete_spec.rb +15 -16
- data/spec/chef/knife/google_disk_list_spec.rb +6 -6
- data/spec/chef/knife/google_region_list_spec.rb +32 -0
- data/spec/chef/knife/google_server_create_spec.rb +78 -44
- data/spec/chef/knife/google_server_delete_spec.rb +37 -37
- data/spec/chef/knife/google_server_list_spec.rb +8 -7
- data/spec/chef/knife/google_setup_spec.rb +1 -2
- data/spec/chef/knife/google_zone_list_spec.rb +4 -4
- data/spec/data/{compute-v1beta15.json → compute-v1.json} +700 -246
- data/spec/data/disk.json +3 -4
- 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/machine_type.json +1 -1
- data/spec/data/network.json +1 -1
- data/spec/data/project.json +1 -1
- data/spec/data/region.json +23 -0
- data/spec/data/serial_port_output.json +1 -1
- data/spec/data/server.json +7 -7
- data/spec/data/snapshot.json +2 -2
- data/spec/data/zone.json +7 -15
- data/spec/data/zone_operation.json +3 -3
- data/spec/google/compute/disk_spec.rb +32 -32
- data/spec/google/compute/firewall_spec.rb +37 -37
- data/spec/google/compute/global_operation_spec.rb +9 -9
- data/spec/google/compute/image_spec.rb +17 -17
- data/spec/google/compute/machine_type_spec.rb +7 -7
- data/spec/google/compute/network_spec.rb +14 -14
- data/spec/google/compute/project_spec.rb +17 -17
- data/spec/google/compute/region_spec.rb +51 -0
- data/spec/google/compute/server_spec.rb +35 -42
- data/spec/google/compute/snapshot_spec.rb +11 -11
- data/spec/google/compute/zone_operation_spec.rb +9 -9
- data/spec/google/compute/zone_spec.rb +7 -7
- data/spec/spec_helper.rb +1 -0
- data/spec/support/mocks.rb +10 -10
- data/spec/support/resource_examples.rb +7 -7
- data/spec/support/spec_google_base.rb +4 -0
- metadata +14 -26
- data/spec/data/kernel.json +0 -15
- 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 => "--
|
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 => "--
|
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
|
-
:
|
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 => "--
|
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 => "--
|
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 => "--
|
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
|
-
:
|
73
|
-
:
|
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(
|
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
|
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][:
|
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][:
|
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],
|
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
|
-
|
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("
|
369
|
-
msg_pair("Image", selflink2name(
|
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"
|