chef-provisioning-google 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +121 -0
- data/Rakefile +22 -0
- data/lib/chef/provider/google_key_pair.rb +172 -0
- data/lib/chef/provisioning/driver_init/google.rb +3 -0
- data/lib/chef/provisioning/google_driver.rb +5 -0
- data/lib/chef/provisioning/google_driver/client/global_operations.rb +18 -0
- data/lib/chef/provisioning/google_driver/client/google_base.rb +72 -0
- data/lib/chef/provisioning/google_driver/client/google_compute_error.rb +16 -0
- data/lib/chef/provisioning/google_driver/client/instance.rb +64 -0
- data/lib/chef/provisioning/google_driver/client/instances.rb +98 -0
- data/lib/chef/provisioning/google_driver/client/metadata.rb +112 -0
- data/lib/chef/provisioning/google_driver/client/operation.rb +23 -0
- data/lib/chef/provisioning/google_driver/client/operations_base.rb +44 -0
- data/lib/chef/provisioning/google_driver/client/projects.rb +39 -0
- data/lib/chef/provisioning/google_driver/client/zone_operations.rb +18 -0
- data/lib/chef/provisioning/google_driver/credentials.rb +63 -0
- data/lib/chef/provisioning/google_driver/driver.rb +313 -0
- data/lib/chef/provisioning/google_driver/resources.rb +0 -0
- data/lib/chef/provisioning/google_driver/version.rb +7 -0
- data/lib/chef/resource/google_key_pair.rb +50 -0
- data/spec/chef/provisioning/google_driver/client/instances_spec.rb +205 -0
- data/spec/chef/provisioning/google_driver/client/operations_spec.rb +62 -0
- data/spec/chef/provisioning/google_driver/client/projects_spec.rb +162 -0
- data/spec/chef/provisioning/google_driver/client/services_helper.rb +33 -0
- data/spec/spec_helper.rb +0 -0
- metadata +222 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative "google_base"
|
2
|
+
require_relative "metadata"
|
3
|
+
|
4
|
+
class Chef
|
5
|
+
module Provisioning
|
6
|
+
module GoogleDriver
|
7
|
+
module Client
|
8
|
+
# Wraps a Projects service of the GCE API.
|
9
|
+
class Projects < GoogleBase
|
10
|
+
|
11
|
+
def get
|
12
|
+
# The default arguments are already enough for this call.
|
13
|
+
response = make_request(compute.projects.get)
|
14
|
+
raise_if_error(response)
|
15
|
+
Metadata.new(response)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Takes a metadata object retrieved via #get and updates the metadata on GCE
|
19
|
+
# using the projects.set_common_instance_metadata API call.
|
20
|
+
# This fails if the metadata on GCE has been updated since the passed
|
21
|
+
# metadata object has been retrieved via #get.
|
22
|
+
# Note that this omits the API call if the metadata object hasn't changed
|
23
|
+
# locally since it was retrieved via #get.
|
24
|
+
def set_common_instance_metadata(metadata)
|
25
|
+
return nil unless metadata.changed?
|
26
|
+
response = make_request(
|
27
|
+
compute.projects.set_common_instance_metadata,
|
28
|
+
# Default paremeters are sufficient.
|
29
|
+
{},
|
30
|
+
{ items: metadata.items, fingerprint: metadata.fingerprint }
|
31
|
+
)
|
32
|
+
operation_response(response)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative "operations_base"
|
2
|
+
|
3
|
+
class Chef
|
4
|
+
module Provisioning
|
5
|
+
module GoogleDriver
|
6
|
+
module Client
|
7
|
+
# Wraps a ZoneOperations service of the GCE API.
|
8
|
+
class ZoneOperations < OperationsBase
|
9
|
+
|
10
|
+
def operations_service
|
11
|
+
compute.zone_operations
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Chef
|
2
|
+
module Provisioning
|
3
|
+
module GoogleDriver
|
4
|
+
# Access various forms of Google credentials
|
5
|
+
# TODO: load credentials from metadata server when provisioning from a GCE machine
|
6
|
+
class Credentials
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@credentials = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](name)
|
13
|
+
@credentials[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(name, value)
|
17
|
+
@credentials[name] = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_hash(h)
|
21
|
+
credentials = self.new
|
22
|
+
h.each do |k, v|
|
23
|
+
credentials[k] = v
|
24
|
+
end
|
25
|
+
credentials.validate!
|
26
|
+
credentials
|
27
|
+
end
|
28
|
+
|
29
|
+
# Validates whether the key settings are present in the credential object and keys are in the correct format.
|
30
|
+
# If no client_email is specified, method will try to load the client_email from the json key.
|
31
|
+
def validate!
|
32
|
+
unless self[:p12_key_path] || self[:json_key_path]
|
33
|
+
raise "Google key path is missing. Options provided: #{self.inspect}"
|
34
|
+
end
|
35
|
+
if self[:json_key_path]
|
36
|
+
json_key_hash = JSON.load(File.open(self[:json_key_path]))
|
37
|
+
|
38
|
+
unless self[:google_client_email]
|
39
|
+
# Try to load client_email from json if not present
|
40
|
+
if json_key_hash["client_email"]
|
41
|
+
self[:google_client_email] = json_key_hash["client_email"]
|
42
|
+
else
|
43
|
+
raise "google_client_email must be specified"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
raise "Invalid Google JSON key, no private key" unless json_key_hash.include?("private_key")
|
48
|
+
elsif self[:p12_key_path]
|
49
|
+
raise "p12 key doesn't exist in the path specified" unless File.exist?(self[:p12_key_path])
|
50
|
+
raise "google_client_email must be specified" unless self[:google_client_email]
|
51
|
+
else
|
52
|
+
raise "json_key_path or p12_key_path is missing. Options provided: #{self}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_defaults
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require "chef/provisioning/driver"
|
2
|
+
require "chef/provisioning/google_driver/credentials"
|
3
|
+
require "chef/provisioning/google_driver/version"
|
4
|
+
require "chef/mixin/deep_merge"
|
5
|
+
require "chef/provisioning/convergence_strategy/install_cached"
|
6
|
+
require "chef/provisioning/convergence_strategy/install_sh"
|
7
|
+
require "chef/provisioning/convergence_strategy/install_msi"
|
8
|
+
require "chef/provisioning/convergence_strategy/no_converge"
|
9
|
+
require "chef/provisioning/transport/ssh"
|
10
|
+
require "chef/provisioning/transport/winrm"
|
11
|
+
require "chef/provisioning/machine/windows_machine"
|
12
|
+
require "chef/provisioning/machine/unix_machine"
|
13
|
+
require "chef/provisioning/machine_spec"
|
14
|
+
|
15
|
+
require_relative "client/instances"
|
16
|
+
require_relative "client/global_operations"
|
17
|
+
require_relative "client/projects"
|
18
|
+
require_relative "client/zone_operations"
|
19
|
+
|
20
|
+
require "google/api_client"
|
21
|
+
require "retryable"
|
22
|
+
require "etc"
|
23
|
+
|
24
|
+
class Chef
|
25
|
+
module Provisioning
|
26
|
+
module GoogleDriver
|
27
|
+
# Provisions machines using the Google SDK
|
28
|
+
# TODO look at the superclass comments for further explanation of the overridden methods in this class
|
29
|
+
class Driver < Chef::Provisioning::Driver
|
30
|
+
|
31
|
+
include Chef::Mixin::DeepMerge
|
32
|
+
|
33
|
+
attr_reader :google, :zone, :project, :instance_client, :global_operations_client, :zone_operations_client, :project_client
|
34
|
+
URL_REGEX = /^google:(.+?):(.+)$/
|
35
|
+
|
36
|
+
# URL scheme:
|
37
|
+
# google:zone
|
38
|
+
def self.from_url(driver_url, config)
|
39
|
+
self.new(driver_url, config)
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(driver_url, config)
|
43
|
+
super
|
44
|
+
|
45
|
+
m = URL_REGEX.match(driver_url)
|
46
|
+
if m.nil?
|
47
|
+
raise "Driver URL [#{driver_url}] must match #{URL_REGEX.inspect}"
|
48
|
+
end
|
49
|
+
# TODO: Move zone into bootstrap_options
|
50
|
+
@zone = m[1]
|
51
|
+
@project = m[2]
|
52
|
+
|
53
|
+
@google = Google::APIClient.new(
|
54
|
+
:application_name => "chef-provisioning-google",
|
55
|
+
:application_version => Chef::Provisioning::GoogleDriver::VERSION
|
56
|
+
)
|
57
|
+
if google_credentials[:p12_key_path]
|
58
|
+
signing_key = Google::APIClient::KeyUtils.load_from_pkcs12(google_credentials[:p12_key_path], "notasecret")
|
59
|
+
elsif google_credentials[:json_key_path]
|
60
|
+
json_private_key = JSON.load(File.open(google_credentials[:json_key_path]))["private_key"]
|
61
|
+
signing_key = Google::APIClient::KeyUtils.load_from_pem(json_private_key, "notasecret")
|
62
|
+
end
|
63
|
+
google.authorization = Signet::OAuth2::Client.new(
|
64
|
+
:token_credential_uri => "https://accounts.google.com/o/oauth2/token",
|
65
|
+
:audience => "https://accounts.google.com/o/oauth2/token",
|
66
|
+
:scope => ["https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/compute.readonly"],
|
67
|
+
:issuer => google_credentials[:google_client_email],
|
68
|
+
:signing_key => signing_key
|
69
|
+
)
|
70
|
+
google.authorization.fetch_access_token!
|
71
|
+
|
72
|
+
@instance_client = Client::Instances.new(google, project, zone)
|
73
|
+
@project_client = Client::Projects.new(google, project, zone)
|
74
|
+
@global_operations_client =
|
75
|
+
Client::GlobalOperations.new(google, project, zone)
|
76
|
+
@zone_operations_client =
|
77
|
+
Client::ZoneOperations.new(google, project, zone)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.canonicalize_url(driver_url, config)
|
81
|
+
[ driver_url, config ]
|
82
|
+
end
|
83
|
+
|
84
|
+
def allocate_machine(action_handler, machine_spec, machine_options)
|
85
|
+
# TODO how do we handle running `allocate` and there is already a machine in GCE
|
86
|
+
# but no node in chef? We should just start tracking it.
|
87
|
+
if instance_for(machine_spec).nil?
|
88
|
+
name = machine_spec.name
|
89
|
+
operation = nil
|
90
|
+
action_handler.perform_action "creating instance named #{name} in zone #{zone}" do
|
91
|
+
default_options = instance_client.default_create_options(name)
|
92
|
+
options = hash_only_merge(default_options, machine_options[:insert_options])
|
93
|
+
operation = instance_client.create(options)
|
94
|
+
end
|
95
|
+
zone_operations_client.wait_for_done(action_handler, operation)
|
96
|
+
machine_spec.reference = {
|
97
|
+
"driver_version" => Chef::Provisioning::GoogleDriver::VERSION,
|
98
|
+
"allocated_at" => Time.now.utc.to_s,
|
99
|
+
"host_node" => action_handler.host_node,
|
100
|
+
}
|
101
|
+
machine_spec.driver_url = driver_url
|
102
|
+
# %w(is_windows ssh_username sudo use_private_ip_for_ssh ssh_gateway).each do |key|
|
103
|
+
%w{ssh_username sudo ssh_gateway key_name}.each do |key|
|
104
|
+
machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def ready_machine(action_handler, machine_spec, machine_options)
|
110
|
+
name = machine_spec.name
|
111
|
+
instance = instance_for(machine_spec)
|
112
|
+
|
113
|
+
if instance.nil?
|
114
|
+
raise "Machine #{name} does not have an instance associated with it, or instance does not exist."
|
115
|
+
end
|
116
|
+
|
117
|
+
if !instance.running?
|
118
|
+
# could be PROVISIONING, STAGING, STOPPING, TERMINATED
|
119
|
+
if %w{STOPPING TERMINATED}.include?(instance.status)
|
120
|
+
action_handler.perform_action "instance named #{name} in zone #{zone} was stopped - starting it" do
|
121
|
+
instance_client.start(name)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
instance_client.wait_for_status(action_handler, instance, "RUNNING")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Refresh instance object so we get the new ip address and status
|
128
|
+
instance = instance_for(machine_spec)
|
129
|
+
|
130
|
+
wait_for_transport(action_handler, machine_spec, machine_options, instance)
|
131
|
+
machine_for(machine_spec, machine_options, instance)
|
132
|
+
end
|
133
|
+
|
134
|
+
def destroy_machine(action_handler, machine_spec, machine_options)
|
135
|
+
name = machine_spec.name
|
136
|
+
instance = instance_for(machine_spec)
|
137
|
+
# https://cloud.google.com/compute/docs/instances#checkmachinestatus
|
138
|
+
# TODO Shouldn't we also delete stopped machines?
|
139
|
+
if instance && !%w{STOPPING TERMINATED}.include?(instance.status)
|
140
|
+
operation = nil
|
141
|
+
action_handler.perform_action "destroying instance named #{name} in zone #{zone}" do
|
142
|
+
operation = instance_client.delete(name)
|
143
|
+
end
|
144
|
+
zone_operations_client.wait_for_done(action_handler, operation)
|
145
|
+
end
|
146
|
+
|
147
|
+
strategy = convergence_strategy_for(machine_spec, machine_options)
|
148
|
+
strategy.cleanup_convergence(action_handler, machine_spec)
|
149
|
+
# TODO clean up known_hosts entry
|
150
|
+
end
|
151
|
+
|
152
|
+
def stop_machine(action_handler, machine_spec, machine_options)
|
153
|
+
name = machine_spec.name
|
154
|
+
instance = instance_for(machine_spec)
|
155
|
+
|
156
|
+
if instance.nil?
|
157
|
+
raise "Machine #{name} does not have an instance associated with it, or instance does not exist."
|
158
|
+
end
|
159
|
+
|
160
|
+
unless instance.terminated?
|
161
|
+
unless instance.stopping?
|
162
|
+
action_handler.perform_action "stopping instance named #{name} in zone #{zone}" do
|
163
|
+
instance_client.stop(name)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
instance_client.wait_for_status(action_handler, instance, "TERMINATED")
|
167
|
+
end
|
168
|
+
|
169
|
+
if instance.terminated?
|
170
|
+
Chef::Log.info "Instance #{instance.name} already stopped, nothing to do."
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# TODO make these configurable and find a good place where to put them.
|
175
|
+
def tries
|
176
|
+
Client::GoogleBase::TRIES
|
177
|
+
end
|
178
|
+
|
179
|
+
def sleep
|
180
|
+
Client::GoogleBase::SLEEP_SECONDS
|
181
|
+
end
|
182
|
+
|
183
|
+
def wait_for_transport(action_handler, machine_spec, machine_options, instance)
|
184
|
+
transport = transport_for(machine_spec, machine_options, instance)
|
185
|
+
unless transport.available?
|
186
|
+
if action_handler.should_perform_actions
|
187
|
+
Retryable.retryable(:tries => tries, :sleep => sleep, :matching => /Not done/) do |retries, exception|
|
188
|
+
action_handler.report_progress(" waited #{retries * sleep}/#{tries * sleep}s for instance #{instance.name} to be connectable (transport up and running) ...")
|
189
|
+
raise "Not done" unless transport.available?
|
190
|
+
end
|
191
|
+
action_handler.report_progress "#{machine_spec.name} is now connectable"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def transport_for(machine_spec, machine_options, instance)
|
197
|
+
# TODO winrm
|
198
|
+
# if machine_spec.reference['is_windows']
|
199
|
+
# create_winrm_transport(machine_spec, machine_options, instance)
|
200
|
+
# else
|
201
|
+
create_ssh_transport(machine_spec, machine_options, instance)
|
202
|
+
# end
|
203
|
+
end
|
204
|
+
|
205
|
+
def create_ssh_transport(machine_spec, machine_options, instance)
|
206
|
+
ssh_options = ssh_options_for(machine_spec, machine_options, instance)
|
207
|
+
username = machine_spec.reference["ssh_username"] || machine_options[:ssh_username] || Etc.getlogin
|
208
|
+
if machine_options.has_key?(:ssh_username) && machine_options[:ssh_username] != machine_spec.reference["ssh_username"]
|
209
|
+
Chef::Log.warn("Server #{machine_spec.name} was created with SSH username #{machine_spec.reference['ssh_username']} and machine_options specifies username #{machine_options[:ssh_username]}. Using #{machine_spec.reference['ssh_username']}. Please edit the node and change the chef_provisioning.reference.ssh_username attribute if you want to change it.")
|
210
|
+
end
|
211
|
+
options = {}
|
212
|
+
if machine_spec.reference[:sudo] || (!machine_spec.reference.has_key?(:sudo) && username != "root")
|
213
|
+
options[:prefix] = "sudo "
|
214
|
+
end
|
215
|
+
|
216
|
+
remote_host = instance.determine_remote_host
|
217
|
+
|
218
|
+
#Enable pty by default
|
219
|
+
options[:ssh_pty_enable] = true
|
220
|
+
options[:ssh_gateway] = machine_spec.reference["ssh_gateway"] if machine_spec.reference.has_key?("ssh_gateway")
|
221
|
+
|
222
|
+
Chef::Provisioning::Transport::SSH.new(remote_host, username, ssh_options, options, config)
|
223
|
+
end
|
224
|
+
|
225
|
+
def ssh_options_for(machine_spec, machine_options, instance)
|
226
|
+
result = {
|
227
|
+
:auth_methods => [ "publickey" ],
|
228
|
+
:keys_only => true,
|
229
|
+
:host_key_alias => "#{instance.id}.GOOGLE",
|
230
|
+
}.merge(machine_options[:ssh_options] || {})
|
231
|
+
# TODO right now we only allow keys created for the whole project and specified in the
|
232
|
+
# bootstrap options - look at AWS for other options
|
233
|
+
if machine_options[:key_name]
|
234
|
+
# TODO how do I add keys to config[:private_keys] ?
|
235
|
+
# result[:key_data] = [ get_private_key(machine_options[:key_name]) ]
|
236
|
+
# TODO: what to do if we find multiple valid keys in config[:private_key_paths] ?
|
237
|
+
config[:private_key_paths].each do |path|
|
238
|
+
result[:key_data] = IO.read("#{path}/#{machine_options[:key_name]}") if File.exist?("#{path}/#{machine_options[:key_name]}")
|
239
|
+
end
|
240
|
+
unless result[:key_data]
|
241
|
+
raise "#{machine_options[:key_name]} doesn't exist in private_key_paths:#{config[:private_key_paths]}"
|
242
|
+
end
|
243
|
+
else
|
244
|
+
raise "No key found to connect to #{machine_spec.name} (#{machine_spec.reference.inspect})!"
|
245
|
+
end
|
246
|
+
result
|
247
|
+
end
|
248
|
+
|
249
|
+
def machine_for(machine_spec, machine_options, instance = nil)
|
250
|
+
instance ||= instance_for(machine_spec)
|
251
|
+
|
252
|
+
unless instance
|
253
|
+
raise "Instance for node #{machine_spec.name} has not been created!"
|
254
|
+
end
|
255
|
+
|
256
|
+
# TODO winrm
|
257
|
+
# if machine_spec.reference['is_windows']
|
258
|
+
# Chef::Provisioning::Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, instance), convergence_strategy_for(machine_spec, machine_options))
|
259
|
+
# else
|
260
|
+
Chef::Provisioning::Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, instance), convergence_strategy_for(machine_spec, machine_options))
|
261
|
+
# end
|
262
|
+
end
|
263
|
+
|
264
|
+
def convergence_strategy_for(machine_spec, machine_options)
|
265
|
+
# Tell Ohai that this is an EC2 instance so that it runs the EC2 plugin
|
266
|
+
convergence_options = Cheffish::MergedConfig.new(
|
267
|
+
machine_options[:convergence_options] || {},
|
268
|
+
# TODO what is the right ohai hints file?
|
269
|
+
ohai_hints: { "google" => "" })
|
270
|
+
|
271
|
+
# Defaults
|
272
|
+
unless machine_spec.reference
|
273
|
+
return Chef::Provisioning::ConvergenceStrategy::NoConverge.new(convergence_options, config)
|
274
|
+
end
|
275
|
+
|
276
|
+
# TODO winrm
|
277
|
+
# if machine_spec.reference['is_windows']
|
278
|
+
# Chef::Provisioning::ConvergenceStrategy::InstallMsi.new(convergence_options, config)
|
279
|
+
if machine_options[:cached_installer] == true
|
280
|
+
Chef::Provisioning::ConvergenceStrategy::InstallCached.new(convergence_options, config)
|
281
|
+
else
|
282
|
+
Chef::Provisioning::ConvergenceStrategy::InstallSh.new(convergence_options, config)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def instance_for(machine_spec)
|
287
|
+
if machine_spec.reference
|
288
|
+
if machine_spec.driver_url != driver_url
|
289
|
+
raise "Switching a machine's driver from #{machine_spec.driver_url} to #{driver_url} is not currently supported! Use machine :destroy and then re-create the machine on the new driver."
|
290
|
+
end
|
291
|
+
instance_client.get(machine_spec.name)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
|
297
|
+
# TODO load from file or env variables using common google method
|
298
|
+
# https://cloud.google.com/sdk/gcloud/#gcloud.auth
|
299
|
+
def google_credentials
|
300
|
+
# Grab the list of possible credentials
|
301
|
+
@google_credentials ||= if driver_options[:google_credentials]
|
302
|
+
Credentials.from_hash(driver_options[:google_credentials])
|
303
|
+
else
|
304
|
+
credentials = Credentials.new
|
305
|
+
credentials.load_defaults
|
306
|
+
credentials
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "chef/resource/lwrp_base"
|
2
|
+
|
3
|
+
class Chef::Resource::GoogleKeyPair < Chef::Resource::LWRPBase
|
4
|
+
self.resource_name = self.dsl_name
|
5
|
+
|
6
|
+
# TODO instance specific or project wide?
|
7
|
+
# Right now we only have project wide keys
|
8
|
+
|
9
|
+
provides :google_key_pair
|
10
|
+
|
11
|
+
actions :create, :destroy
|
12
|
+
default_action :create
|
13
|
+
|
14
|
+
# Private key to use as input (will be generated if it does not exist)
|
15
|
+
attribute :private_key_path, :kind_of => String
|
16
|
+
# Public key to use as input (will be generated if it does not exist)
|
17
|
+
attribute :public_key_path, :kind_of => String
|
18
|
+
# List of parameters to the private_key resource used for generation of the key
|
19
|
+
attribute :private_key_options, :kind_of => Hash
|
20
|
+
|
21
|
+
# This applies to both the local keys and the remote key
|
22
|
+
attribute :allow_overwrite, :kind_of => [TrueClass, FalseClass], :default => false
|
23
|
+
|
24
|
+
# TODO: add a `user` attribute which sets the user to login with the key
|
25
|
+
# GCE uses the key user to create a user on the instance, which may duplicate
|
26
|
+
# some logic the user is trying to do with their chef recipes
|
27
|
+
|
28
|
+
def after_created
|
29
|
+
# We default these here so load_current_resource can diff
|
30
|
+
if private_key_path.nil?
|
31
|
+
private_key_path ::File.join(driver.config[:private_key_write_path], "google_default")
|
32
|
+
elsif Pathname.new(private_key_path).relative?
|
33
|
+
private_key_path ::File.join(driver.config[:private_key_write_path], private_key_path)
|
34
|
+
end
|
35
|
+
# TODO you don't actually need to write the private key to disc if it isn't provided
|
36
|
+
# it can be read from the private key, but this code update needs testing
|
37
|
+
if public_key_path.nil?
|
38
|
+
public_key_path ::File.join(driver.config[:private_key_write_path], "google_default.pub")
|
39
|
+
elsif Pathname.new(public_key_path).relative?
|
40
|
+
public_key_path ::File.join(driver.config[:private_key_write_path], public_key_path)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO introduce base class and add this as attribute, like AWS does
|
45
|
+
# Ideally, we won't be creating lots of copies of the same driver object, but it is okay
|
46
|
+
# if we do - they aren't singletons
|
47
|
+
def driver
|
48
|
+
run_context.chef_provisioning.driver_for(run_context.chef_provisioning.current_driver)
|
49
|
+
end
|
50
|
+
end
|