knife-google 1.3.1 → 2.0.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.
- checksums.yaml +5 -13
- data/.gitignore +1 -0
- data/.travis.yml +15 -3
- data/CHANGELOG.md +4 -6
- data/Gemfile +3 -9
- data/README.md +208 -355
- data/RELEASE_NOTES.md +8 -17
- data/Rakefile +8 -49
- data/knife-google.gemspec +20 -17
- data/lib/chef/knife/cloud/google_service.rb +491 -0
- data/lib/chef/knife/cloud/google_service_helpers.rb +62 -0
- data/lib/chef/knife/cloud/google_service_options.rb +58 -0
- data/lib/chef/knife/google_disk_create.rb +40 -44
- data/lib/chef/knife/google_disk_delete.rb +22 -40
- data/lib/chef/knife/google_disk_list.rb +57 -51
- data/lib/chef/knife/google_project_quotas.rb +59 -0
- data/lib/chef/knife/google_region_list.rb +43 -102
- data/lib/chef/knife/google_region_quotas.rb +77 -0
- data/lib/chef/knife/google_server_create.rb +224 -505
- data/lib/chef/knife/google_server_delete.rb +20 -78
- data/lib/chef/knife/google_server_list.rb +42 -53
- data/lib/chef/knife/google_server_show.rb +44 -0
- data/lib/chef/knife/google_zone_list.rb +39 -50
- data/lib/knife-google/version.rb +3 -2
- data/spec/cloud/google_service_helpers_spec.rb +120 -0
- data/spec/cloud/google_service_spec.rb +832 -0
- data/spec/google_disk_create_spec.rb +72 -0
- data/spec/google_disk_delete_spec.rb +64 -0
- data/spec/google_disk_list_spec.rb +93 -0
- data/spec/google_project_quotas_spec.rb +63 -0
- data/spec/google_region_list_spec.rb +65 -0
- data/spec/google_region_quotas_spec.rb +108 -0
- data/spec/google_server_create_spec.rb +177 -0
- data/spec/google_server_delete_spec.rb +39 -0
- data/spec/google_server_list_spec.rb +77 -0
- data/spec/google_server_show_spec.rb +60 -0
- data/spec/google_zone_list_spec.rb +59 -0
- metadata +91 -114
- data/CONTRIB.md +0 -64
- data/lib/chef/knife/google_base.rb +0 -76
- data/lib/chef/knife/google_project_list.rb +0 -178
- data/lib/chef/knife/google_setup.rb +0 -31
- data/lib/google/compute.rb +0 -47
- data/lib/google/compute/client.rb +0 -216
- data/lib/google/compute/config.rb +0 -23
- data/lib/google/compute/creatable_resource_collection.rb +0 -55
- data/lib/google/compute/deletable_resource_collection.rb +0 -51
- data/lib/google/compute/disk.rb +0 -38
- data/lib/google/compute/exception.rb +0 -30
- data/lib/google/compute/firewall.rb +0 -65
- data/lib/google/compute/global_operation.rb +0 -60
- data/lib/google/compute/image.rb +0 -29
- data/lib/google/compute/listable_resource_collection.rb +0 -33
- data/lib/google/compute/machine_type.rb +0 -36
- data/lib/google/compute/mixins/utils.rb +0 -58
- data/lib/google/compute/network.rb +0 -29
- data/lib/google/compute/project.rb +0 -76
- data/lib/google/compute/region.rb +0 -31
- data/lib/google/compute/region_operation.rb +0 -62
- data/lib/google/compute/resource.rb +0 -81
- data/lib/google/compute/resource_collection.rb +0 -78
- data/lib/google/compute/server.rb +0 -88
- data/lib/google/compute/server/attached_disk.rb +0 -39
- data/lib/google/compute/server/network_interface.rb +0 -38
- data/lib/google/compute/server/network_interface/access_config.rb +0 -35
- data/lib/google/compute/server/serial_port_output.rb +0 -31
- data/lib/google/compute/snapshot.rb +0 -30
- data/lib/google/compute/version.rb +0 -19
- data/lib/google/compute/zone.rb +0 -34
- data/lib/google/compute/zone_operation.rb +0 -62
- data/spec/chef/knife/google_base_spec.rb +0 -46
- data/spec/chef/knife/google_disk_create_spec.rb +0 -37
- data/spec/chef/knife/google_disk_delete_spec.rb +0 -64
- data/spec/chef/knife/google_disk_list_spec.rb +0 -36
- data/spec/chef/knife/google_region_list_spec.rb +0 -32
- data/spec/chef/knife/google_server_create_spec.rb +0 -138
- data/spec/chef/knife/google_server_delete_spec.rb +0 -127
- data/spec/chef/knife/google_server_list_spec.rb +0 -39
- data/spec/chef/knife/google_setup_spec.rb +0 -24
- data/spec/chef/knife/google_zone_list_spec.rb +0 -32
- data/spec/data/client.json +0 -14
- data/spec/data/compute-v1.json +0 -6734
- data/spec/data/disk.json +0 -14
- data/spec/data/firewall.json +0 -13
- data/spec/data/global_operation.json +0 -36
- data/spec/data/image.json +0 -12
- data/spec/data/machine_type.json +0 -24
- data/spec/data/network.json +0 -10
- data/spec/data/project.json +0 -21
- data/spec/data/region.json +0 -23
- data/spec/data/serial_port_output.json +0 -5
- data/spec/data/server.json +0 -46
- data/spec/data/snapshot.json +0 -12
- data/spec/data/zone.json +0 -22
- data/spec/data/zone_operation.json +0 -36
- data/spec/google/compute/disk_spec.rb +0 -115
- data/spec/google/compute/firewall_spec.rb +0 -129
- data/spec/google/compute/global_operation_spec.rb +0 -62
- data/spec/google/compute/image_spec.rb +0 -75
- data/spec/google/compute/machine_type_spec.rb +0 -53
- data/spec/google/compute/network_spec.rb +0 -68
- data/spec/google/compute/project_spec.rb +0 -71
- data/spec/google/compute/region_spec.rb +0 -51
- data/spec/google/compute/server_spec.rb +0 -118
- data/spec/google/compute/snapshot_spec.rb +0 -57
- data/spec/google/compute/zone_operation_spec.rb +0 -62
- data/spec/google/compute/zone_spec.rb +0 -51
- data/spec/spec_helper.rb +0 -45
- data/spec/support/mocks.rb +0 -62
- data/spec/support/resource_examples.rb +0 -70
- data/spec/support/spec_google_base.rb +0 -60
data/RELEASE_NOTES.md
CHANGED
|
@@ -6,22 +6,13 @@ Example Note:
|
|
|
6
6
|
## Example Heading
|
|
7
7
|
Details about the thing that changed that needs to get included in the Release Notes in markdown.
|
|
8
8
|
-->
|
|
9
|
-
# knife-google
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
knife-google plug-in, you should consider upgrading to this version.
|
|
9
|
+
# knife-google 2.0.0 Release Notes :
|
|
10
|
+
In this release of knife-google the option names have been changed to conform to the long option names.
|
|
11
|
+
For example the `zone` option has been changed to `--gce-zone` as per the long option.
|
|
12
|
+
Also, support has been added so that the options can be set from knife.rb file.
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
## Issues fixed in knife-google 2.0.0
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
https://
|
|
19
|
-
https://github.com/
|
|
20
|
-
|
|
21
|
-
## Issues fixed in knife-google 1.3.1
|
|
22
|
-
|
|
23
|
-
* KNIFE-473: knife-google should refresh access token
|
|
24
|
-
|
|
25
|
-
## knife-google Breaking Changes:
|
|
26
|
-
|
|
27
|
-
None.
|
|
16
|
+
* [knife-google #70] (https://github.com/chef/knife-google/pull/70) Fix for --bootstrap-version command line option
|
|
17
|
+
* [knife-google #76] (https://github.com/chef/knife-google/pull/76) Fix for picking options from knife.rb
|
|
18
|
+
* [knife-google #77] (https://github.com/chef/knife-google/pull/77) Changed option names according to long options
|
data/Rakefile
CHANGED
|
@@ -1,53 +1,12 @@
|
|
|
1
|
-
|
|
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
|
-
# encoding: utf-8
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rspec/core/rake_task"
|
|
16
3
|
|
|
17
|
-
|
|
18
|
-
require 'rake'
|
|
19
|
-
GEM_NAME = "knife-google"
|
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
20
5
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Gem::PackageTask.new(spec) do |pkg|
|
|
26
|
-
pkg.gem_spec = spec
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
require 'rspec/core'
|
|
30
|
-
require 'rspec/core/rake_task'
|
|
31
|
-
RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
32
|
-
spec.pattern = FileList['spec/**/*_spec.rb']
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
task :default => :spec
|
|
36
|
-
|
|
37
|
-
task :install => :package do
|
|
38
|
-
sh %{gem install pkg/#{GEM_NAME}-#{Knife::Google::VERSION} --no-rdoc --no-ri}
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
task :uninstall do
|
|
42
|
-
sh %{gem uninstall #{GEM_NAME} -x -v #{Knife::Google::VERSION} }
|
|
6
|
+
require "chefstyle"
|
|
7
|
+
require "rubocop/rake_task"
|
|
8
|
+
RuboCop::RakeTask.new(:style) do |task|
|
|
9
|
+
task.options << "--display-cop-names"
|
|
43
10
|
end
|
|
44
11
|
|
|
45
|
-
|
|
46
|
-
Rake::RDocTask.new do |rdoc|
|
|
47
|
-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
|
48
|
-
|
|
49
|
-
rdoc.rdoc_dir = 'rdoc'
|
|
50
|
-
rdoc.title = "#{GEM_NAME} #{version}"
|
|
51
|
-
rdoc.rdoc_files.include('README*', 'LICENSE', 'CONTRIB*')
|
|
52
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
53
|
-
end
|
|
12
|
+
task default: [:spec, :style]
|
data/knife-google.gemspec
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
|
3
|
-
require
|
|
3
|
+
require "knife-google/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |s|
|
|
6
|
-
s.name =
|
|
6
|
+
s.name = "knife-google"
|
|
7
7
|
s.version = Knife::Google::VERSION
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
|
9
|
-
s.authors = ["Chiraq Jog", "Ranjib Dey", "James Tucker", "Paul Rossman", "Eric Johnson"]
|
|
10
|
-
s.
|
|
9
|
+
s.authors = ["Chiraq Jog", "Ranjib Dey", "James Tucker", "Paul Rossman", "Eric Johnson", "Chef Partner Engineering"]
|
|
10
|
+
s.license = "Apache-2.0"
|
|
11
|
+
s.email = ["paulrossman@google.com", "partnereng@chef.io"]
|
|
11
12
|
s.has_rdoc = true
|
|
12
|
-
s.extra_rdoc_files = ["README.md", "LICENSE"
|
|
13
|
-
s.summary = "
|
|
14
|
-
s.description =
|
|
15
|
-
s.homepage = "
|
|
13
|
+
s.extra_rdoc_files = ["README.md", "LICENSE"]
|
|
14
|
+
s.summary = "Google Compute Engine Support for Chef's Knife Command"
|
|
15
|
+
s.description = s.summary
|
|
16
|
+
s.homepage = "https://github.com/chef/knife-google"
|
|
16
17
|
|
|
17
|
-
s.add_dependency "chef",
|
|
18
|
-
s.add_dependency "
|
|
19
|
-
s.add_dependency "
|
|
20
|
-
s.add_dependency "
|
|
21
|
-
s.files = `git ls-files`.split($/)
|
|
22
|
-
#s.files = Dir['CONTRIB.md', 'Gemfile', 'LICENSE', 'README.md', 'Rakefile', 'knife-google.gemspec', 'lib/**/*', 'spec/**/*']
|
|
18
|
+
s.add_dependency "chef", "~> 12.0"
|
|
19
|
+
s.add_dependency "knife-cloud", "~> 1.2.0"
|
|
20
|
+
s.add_dependency "google-api-client", "~> 0.9.0"
|
|
21
|
+
s.add_dependency "gcewinpass", "~> 1.0"
|
|
23
22
|
|
|
24
|
-
s.
|
|
25
|
-
|
|
26
|
-
s.add_development_dependency "
|
|
23
|
+
s.files = `git ls-files -z`.split("\x0")
|
|
24
|
+
|
|
25
|
+
s.add_development_dependency "rake", "~> 10.0"
|
|
26
|
+
s.add_development_dependency "rspec", "~> 3.1"
|
|
27
|
+
s.add_development_dependency "simplecov", "~> 0.9"
|
|
28
|
+
s.add_development_dependency "rubocop"
|
|
29
|
+
s.add_development_dependency "pry"
|
|
27
30
|
end
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
|
3
|
+
# Copyright:: Copyright (c) 2016 Chef Software, Inc.
|
|
4
|
+
# License:: Apache License, Version 2.0
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
require "chef/knife/cloud/exceptions"
|
|
20
|
+
require "chef/knife/cloud/service"
|
|
21
|
+
require "chef/knife/cloud/helpers"
|
|
22
|
+
require "chef/knife/cloud/google_service_helpers"
|
|
23
|
+
require "google/apis/compute_v1"
|
|
24
|
+
require "ipaddr"
|
|
25
|
+
|
|
26
|
+
class Chef::Knife::Cloud
|
|
27
|
+
class GoogleService < Service
|
|
28
|
+
include Chef::Knife::Cloud::GoogleServiceHelpers
|
|
29
|
+
|
|
30
|
+
attr_reader :project, :zone, :wait_time, :refresh_rate, :max_pages, :max_page_size
|
|
31
|
+
|
|
32
|
+
def initialize(options = {})
|
|
33
|
+
@project = options[:project]
|
|
34
|
+
@zone = options[:zone]
|
|
35
|
+
@wait_time = options[:wait_time]
|
|
36
|
+
@refresh_rate = options[:refresh_rate]
|
|
37
|
+
@max_pages = options[:max_pages]
|
|
38
|
+
@max_page_size = options[:max_page_size]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def connection
|
|
42
|
+
return @connection unless @connection.nil?
|
|
43
|
+
|
|
44
|
+
@connection = Google::Apis::ComputeV1::ComputeService.new
|
|
45
|
+
@connection.authorization = authorization
|
|
46
|
+
|
|
47
|
+
@connection
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def authorization
|
|
51
|
+
@authorization ||= Google::Auth.get_application_default(
|
|
52
|
+
[
|
|
53
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
54
|
+
"https://www.googleapis.com/auth/compute",
|
|
55
|
+
]
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def create_server(options = {})
|
|
60
|
+
validate_server_create_options!(options)
|
|
61
|
+
|
|
62
|
+
ui.msg("Creating instance...")
|
|
63
|
+
|
|
64
|
+
instance_object = instance_object_for(options)
|
|
65
|
+
wait_for_operation(connection.insert_instance(project, zone, instance_object))
|
|
66
|
+
wait_for_status("RUNNING") { get_server(options[:name]) }
|
|
67
|
+
|
|
68
|
+
ui.msg("Instance created!")
|
|
69
|
+
|
|
70
|
+
get_server(options[:name])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def delete_server(instance_name)
|
|
74
|
+
begin
|
|
75
|
+
instance = get_server(instance_name)
|
|
76
|
+
rescue Google::Apis::ClientError
|
|
77
|
+
ui.warn("Unable to locate instance #{instance_name} in project #{project}, zone #{zone}")
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
server_summary(instance)
|
|
82
|
+
ui.confirm("Do you really want to delete this instance")
|
|
83
|
+
|
|
84
|
+
ui.msg("Deleting instance #{instance_name}...")
|
|
85
|
+
|
|
86
|
+
wait_for_operation(connection.delete_instance(project, zone, instance_name))
|
|
87
|
+
|
|
88
|
+
ui.msg("Instance #{instance_name} deleted successfully.")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def get_server(instance_name)
|
|
92
|
+
connection.get_instance(project, zone, instance_name)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def create_disk(name, size, type, source_image = nil)
|
|
96
|
+
disk = Google::Apis::ComputeV1::Disk.new
|
|
97
|
+
disk.name = name
|
|
98
|
+
disk.size_gb = size
|
|
99
|
+
disk.type = disk_type_url_for(type)
|
|
100
|
+
|
|
101
|
+
ui.msg("Creating a #{size} GB disk named #{name}...")
|
|
102
|
+
|
|
103
|
+
wait_for_operation(connection.insert_disk(project, zone, disk, source_image: source_image))
|
|
104
|
+
|
|
105
|
+
ui.msg("Waiting for disk to be ready...")
|
|
106
|
+
|
|
107
|
+
wait_for_status("READY") { connection.get_disk(project, zone, name) }
|
|
108
|
+
|
|
109
|
+
ui.msg("Disk created successfully.")
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def delete_disk(name)
|
|
113
|
+
begin
|
|
114
|
+
connection.get_disk(project, zone, name)
|
|
115
|
+
rescue Google::Apis::ClientError
|
|
116
|
+
ui.warn("Unable to locate disk #{name} in project #{project}, zone #{zone}")
|
|
117
|
+
return
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
ui.confirm("Do you really want to delete disk #{name}")
|
|
121
|
+
|
|
122
|
+
ui.msg("Deleting disk #{name}...")
|
|
123
|
+
wait_for_operation(connection.delete_disk(project, zone, name))
|
|
124
|
+
ui.msg("Disk #{name} deleted successfully.")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def list_servers
|
|
128
|
+
instances = paginated_results(:list_instances, :items, project, zone)
|
|
129
|
+
return [] if instances.nil?
|
|
130
|
+
|
|
131
|
+
instances.each_with_object([]) do |instance, memo|
|
|
132
|
+
memo << OpenStruct.new(
|
|
133
|
+
name: instance.name,
|
|
134
|
+
status: instance.status,
|
|
135
|
+
machine_type: machine_type_for(instance),
|
|
136
|
+
network: network_for(instance),
|
|
137
|
+
private_ip: private_ip_for(instance),
|
|
138
|
+
public_ip: public_ip_for(instance)
|
|
139
|
+
)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def list_zones
|
|
144
|
+
paginated_results(:list_zones, :items, project) || []
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def list_disks
|
|
148
|
+
paginated_results(:list_disks, :items, project, zone) || []
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def list_regions
|
|
152
|
+
paginated_results(:list_regions, :items, project) || []
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def list_project_quotas
|
|
156
|
+
connection.get_project(project).quotas || []
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def validate_server_create_options!(options)
|
|
160
|
+
raise "Invalid machine type: #{options[:machine_type]}" unless valid_machine_type?(options[:machine_type])
|
|
161
|
+
raise "Invalid network: #{options[:network]}" unless valid_network?(options[:network])
|
|
162
|
+
raise "Invalid Public IP setting: #{options[:public_ip]}" unless valid_public_ip_setting?(options[:public_ip])
|
|
163
|
+
raise "Invalid image: #{options[:image]} - check your image name, or set an image project if needed" if image_search_for(options[:image], options[:image_project]).nil?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def check_api_call(&block)
|
|
167
|
+
block.call
|
|
168
|
+
rescue Google::Apis::ClientError
|
|
169
|
+
false
|
|
170
|
+
else
|
|
171
|
+
true
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def valid_machine_type?(machine_type)
|
|
175
|
+
return false if machine_type.nil?
|
|
176
|
+
check_api_call { connection.get_machine_type(project, zone, machine_type) }
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def valid_network?(network)
|
|
180
|
+
return false if network.nil?
|
|
181
|
+
check_api_call { connection.get_network(project, network) }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def image_exist?(image_project, image_name)
|
|
185
|
+
check_api_call { connection.get_image(image_project, image_name) }
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def valid_public_ip_setting?(public_ip)
|
|
189
|
+
public_ip.downcase! if public_ip.respond_to?(:downcase)
|
|
190
|
+
|
|
191
|
+
if public_ip.nil? || public_ip == "ephemeral" || public_ip == "none"
|
|
192
|
+
true
|
|
193
|
+
elsif valid_ip_address?(public_ip)
|
|
194
|
+
true
|
|
195
|
+
else
|
|
196
|
+
false
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def valid_ip_address?(ip_address)
|
|
201
|
+
IPAddr.new(ip_address)
|
|
202
|
+
rescue IPAddr::InvalidAddressError
|
|
203
|
+
false
|
|
204
|
+
else
|
|
205
|
+
true
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def instance_object_for(options)
|
|
209
|
+
inst_obj = Google::Apis::ComputeV1::Instance.new
|
|
210
|
+
inst_obj.name = options[:name]
|
|
211
|
+
inst_obj.can_ip_forward = options[:can_ip_forward]
|
|
212
|
+
inst_obj.disks = instance_disks_for(options)
|
|
213
|
+
inst_obj.machine_type = machine_type_url_for(options[:machine_type])
|
|
214
|
+
inst_obj.metadata = instance_metadata_for(options[:metadata])
|
|
215
|
+
inst_obj.network_interfaces = instance_network_interfaces_for(options)
|
|
216
|
+
inst_obj.scheduling = instance_scheduling_for(options)
|
|
217
|
+
inst_obj.service_accounts = instance_service_accounts_for(options) unless instance_service_accounts_for(options).nil?
|
|
218
|
+
inst_obj.tags = instance_tags_for(options[:tags])
|
|
219
|
+
|
|
220
|
+
inst_obj
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def instance_disks_for(options)
|
|
224
|
+
disks = []
|
|
225
|
+
disks << instance_boot_disk_for(options)
|
|
226
|
+
options[:additional_disks].each do |disk_name|
|
|
227
|
+
begin
|
|
228
|
+
disk = connection.get_disk(project, zone, disk_name)
|
|
229
|
+
rescue Google::Apis::ClientError => e
|
|
230
|
+
ui.error("Unable to attach disk #{disk_name} to the instance: #{e.message}")
|
|
231
|
+
raise
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
disks << Google::Apis::ComputeV1::AttachedDisk.new.tap { |x| x.source = disk.self_link }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
disks
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def instance_boot_disk_for(options)
|
|
241
|
+
disk = Google::Apis::ComputeV1::AttachedDisk.new
|
|
242
|
+
params = Google::Apis::ComputeV1::AttachedDiskInitializeParams.new
|
|
243
|
+
|
|
244
|
+
disk.boot = true
|
|
245
|
+
disk.auto_delete = options[:boot_disk_autodelete]
|
|
246
|
+
params.disk_name = boot_disk_name_for(options)
|
|
247
|
+
params.disk_size_gb = options[:boot_disk_size]
|
|
248
|
+
params.disk_type = disk_type_url_for(boot_disk_type_for(options))
|
|
249
|
+
params.source_image = image_search_for(options[:image], options[:image_project])
|
|
250
|
+
|
|
251
|
+
disk.initialize_params = params
|
|
252
|
+
disk
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def boot_disk_type_for(options)
|
|
256
|
+
options[:boot_disk_ssd] ? "pd-ssd" : "pd-standard"
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def image_search_for(image, image_project)
|
|
260
|
+
# if the user provided an image_project, assume they want it, no questions asked
|
|
261
|
+
return image_url_for(image_project, image) unless image_project.nil?
|
|
262
|
+
|
|
263
|
+
# no image project has been provided. We'll first check the user's project for the image.
|
|
264
|
+
url = image_url_for(project, image)
|
|
265
|
+
return url unless url.nil?
|
|
266
|
+
|
|
267
|
+
# Image not found in user's project. Is there a public project this image might exist in?
|
|
268
|
+
public_project = public_project_for_image(image)
|
|
269
|
+
if public_project
|
|
270
|
+
return image_url_for(public_project, image)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# No image in user's project or public project, so it doesn't exist.
|
|
274
|
+
nil
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def image_url_for(image_project, image_name)
|
|
278
|
+
return "projects/#{image_project}/global/images/#{image_name}" if image_exist?(image_project, image_name)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def boot_disk_name_for(options)
|
|
282
|
+
options[:boot_disk_name].nil? ? options[:name] : options[:boot_disk_name]
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def machine_type_url_for(machine_type)
|
|
286
|
+
"zones/#{zone}/machineTypes/#{machine_type}"
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def instance_metadata_for(metadata)
|
|
290
|
+
return if metadata.nil? || metadata.empty?
|
|
291
|
+
|
|
292
|
+
metadata_obj = Google::Apis::ComputeV1::Metadata.new
|
|
293
|
+
metadata_obj.items = metadata.each_with_object([]) do |(k, v), memo|
|
|
294
|
+
metadata_item = Google::Apis::ComputeV1::Metadata::Item.new
|
|
295
|
+
metadata_item.key = k
|
|
296
|
+
metadata_item.value = v
|
|
297
|
+
|
|
298
|
+
memo << metadata_item
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
metadata_obj
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def instance_network_interfaces_for(options)
|
|
305
|
+
interface = Google::Apis::ComputeV1::NetworkInterface.new
|
|
306
|
+
interface.network = network_url_for(options[:network])
|
|
307
|
+
interface.access_configs = instance_access_configs_for(options[:public_ip])
|
|
308
|
+
|
|
309
|
+
Array(interface)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def instance_access_configs_for(public_ip)
|
|
313
|
+
return [] if public_ip.nil? || public_ip == "NONE"
|
|
314
|
+
|
|
315
|
+
access_config = Google::Apis::ComputeV1::AccessConfig.new
|
|
316
|
+
access_config.name = "External NAT"
|
|
317
|
+
access_config.type = "ONE_TO_ONE_NAT"
|
|
318
|
+
access_config.nat_ip = public_ip if valid_ip_address?(public_ip)
|
|
319
|
+
|
|
320
|
+
Array(access_config)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def network_url_for(network)
|
|
324
|
+
"projects/#{project}/global/networks/#{network}"
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
def instance_scheduling_for(options)
|
|
328
|
+
scheduling = Google::Apis::ComputeV1::Scheduling.new
|
|
329
|
+
scheduling.automatic_restart = options[:auto_restart].to_s
|
|
330
|
+
scheduling.on_host_maintenance = migrate_setting_for(options[:auto_migrate])
|
|
331
|
+
|
|
332
|
+
scheduling
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def migrate_setting_for(auto_migrate)
|
|
336
|
+
auto_migrate ? "MIGRATE" : "TERMINATE"
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def instance_service_accounts_for(options)
|
|
340
|
+
return if options[:service_account_scopes].nil? || options[:service_account_scopes].empty?
|
|
341
|
+
|
|
342
|
+
service_account = Google::Apis::ComputeV1::ServiceAccount.new
|
|
343
|
+
service_account.email = options[:service_account_name]
|
|
344
|
+
service_account.scopes = options[:service_account_scopes].map { |scope| "https://www.googleapis.com/auth/#{scope}" unless scope.start_with?("https://www.googleapis.com/auth/") }
|
|
345
|
+
|
|
346
|
+
Array(service_account)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def instance_tags_for(tags)
|
|
350
|
+
return if tags.nil? || tags.empty?
|
|
351
|
+
|
|
352
|
+
tag_obj = Google::Apis::ComputeV1::Tags.new
|
|
353
|
+
tag_obj.items = tags
|
|
354
|
+
|
|
355
|
+
tag_obj
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def network_for(instance)
|
|
359
|
+
instance.network_interfaces.first.network.split("/").last
|
|
360
|
+
rescue NoMethodError
|
|
361
|
+
"unknown"
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def machine_type_for(instance)
|
|
365
|
+
instance.machine_type.split("/").last
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def server_summary(server, _columns_with_info = nil)
|
|
369
|
+
msg_pair("Instance Name", server.name)
|
|
370
|
+
msg_pair("Status", server.status)
|
|
371
|
+
msg_pair("Machine Type", machine_type_for(server))
|
|
372
|
+
msg_pair("Project", project)
|
|
373
|
+
msg_pair("Zone", zone)
|
|
374
|
+
msg_pair("Network", network_for(server))
|
|
375
|
+
msg_pair("Private IP", private_ip_for(server))
|
|
376
|
+
msg_pair("Public IP", public_ip_for(server))
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def public_project_for_image(image)
|
|
380
|
+
case image
|
|
381
|
+
when /centos/
|
|
382
|
+
"centos-cloud"
|
|
383
|
+
when /container-vm/
|
|
384
|
+
"google-containers"
|
|
385
|
+
when /coreos/
|
|
386
|
+
"coreos-cloud"
|
|
387
|
+
when /debian/
|
|
388
|
+
"debian-cloud"
|
|
389
|
+
when /opensuse-cloud/
|
|
390
|
+
"opensuse-cloud"
|
|
391
|
+
when /rhel/
|
|
392
|
+
"rhel-cloud"
|
|
393
|
+
when /sles/
|
|
394
|
+
"suse-cloud"
|
|
395
|
+
when /ubuntu/
|
|
396
|
+
"ubuntu-os-cloud"
|
|
397
|
+
when /windows/
|
|
398
|
+
"windows-cloud"
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def disk_type_url_for(type)
|
|
403
|
+
"zones/#{zone}/diskTypes/#{type}"
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def paginated_results(api_method, items_method, *args)
|
|
407
|
+
items = []
|
|
408
|
+
next_token = nil
|
|
409
|
+
loop_num = 0
|
|
410
|
+
|
|
411
|
+
loop do
|
|
412
|
+
loop_num += 1
|
|
413
|
+
|
|
414
|
+
response = connection.send(api_method.to_sym, *args, max_results: max_page_size, page_token: next_token)
|
|
415
|
+
response_items = response.send(items_method.to_sym)
|
|
416
|
+
|
|
417
|
+
break if response_items.nil?
|
|
418
|
+
|
|
419
|
+
items += response_items
|
|
420
|
+
|
|
421
|
+
next_token = response.next_page_token
|
|
422
|
+
break if next_token.nil?
|
|
423
|
+
|
|
424
|
+
if loop_num >= max_pages
|
|
425
|
+
ui.warn("Max pages (#{max_pages}) reached, but more results exist - truncating results...")
|
|
426
|
+
break
|
|
427
|
+
end
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
items
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def wait_for_status(requested_status, &block)
|
|
434
|
+
last_status = ""
|
|
435
|
+
|
|
436
|
+
begin
|
|
437
|
+
Timeout.timeout(wait_time) do
|
|
438
|
+
loop do
|
|
439
|
+
item = block.call
|
|
440
|
+
current_status = item.status
|
|
441
|
+
|
|
442
|
+
if current_status == requested_status
|
|
443
|
+
print "\n"
|
|
444
|
+
break
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
if last_status == current_status
|
|
448
|
+
print "."
|
|
449
|
+
else
|
|
450
|
+
last_status = current_status
|
|
451
|
+
print "\n"
|
|
452
|
+
print "Current status: #{current_status}."
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
sleep refresh_rate
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
rescue Timeout::Error
|
|
459
|
+
ui.msg("")
|
|
460
|
+
ui.error("Request did not complete in #{wait_time} seconds. Check the Google Cloud Console for more info.")
|
|
461
|
+
exit 1
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
def wait_for_operation(operation)
|
|
466
|
+
operation_name = operation.name
|
|
467
|
+
|
|
468
|
+
wait_for_status("DONE") { zone_operation(operation_name) }
|
|
469
|
+
|
|
470
|
+
errors = operation_errors(operation_name)
|
|
471
|
+
return if errors.empty?
|
|
472
|
+
|
|
473
|
+
errors.each do |error|
|
|
474
|
+
ui.error("#{ui.color(error.code, :bold)}: #{error.message}")
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
raise "Operation #{operation_name} failed."
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def zone_operation(operation_name)
|
|
481
|
+
connection.get_zone_operation(project, zone, operation_name)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def operation_errors(operation_name)
|
|
485
|
+
operation = zone_operation(operation_name)
|
|
486
|
+
return [] if operation.error.nil?
|
|
487
|
+
|
|
488
|
+
operation.error.errors
|
|
489
|
+
end
|
|
490
|
+
end
|
|
491
|
+
end
|