chef-provisioning-fog 0.26.1 → 0.26.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +29 -8
  3. data/Rakefile +23 -12
  4. data/chef-provisioning-fog.gemspec +24 -27
  5. data/lib/chef/provider/fog_key_pair.rb +49 -53
  6. data/lib/chef/provider/scaleway_volume.rb +46 -48
  7. data/lib/chef/provisioning/driver_init/fog.rb +1 -1
  8. data/lib/chef/provisioning/fog_driver/driver.rb +646 -653
  9. data/lib/chef/provisioning/fog_driver/providers/aws.rb +411 -422
  10. data/lib/chef/provisioning/fog_driver/providers/aws/credentials.rb +88 -90
  11. data/lib/chef/provisioning/fog_driver/providers/cloudstack.rb +32 -34
  12. data/lib/chef/provisioning/fog_driver/providers/digitalocean.rb +98 -100
  13. data/lib/chef/provisioning/fog_driver/providers/google.rb +27 -34
  14. data/lib/chef/provisioning/fog_driver/providers/joyent.rb +53 -55
  15. data/lib/chef/provisioning/fog_driver/providers/openstack.rb +139 -146
  16. data/lib/chef/provisioning/fog_driver/providers/rackspace.rb +40 -44
  17. data/lib/chef/provisioning/fog_driver/providers/scaleway.rb +183 -189
  18. data/lib/chef/provisioning/fog_driver/providers/softlayer.rb +61 -64
  19. data/lib/chef/provisioning/fog_driver/providers/vcair.rb +72 -78
  20. data/lib/chef/provisioning/fog_driver/providers/xenserver.rb +56 -69
  21. data/lib/chef/provisioning/fog_driver/recipe_dsl.rb +11 -12
  22. data/lib/chef/provisioning/fog_driver/version.rb +1 -1
  23. data/lib/chef/resource/fog_key_pair.rb +8 -8
  24. data/lib/chef/resource/scaleway_volume.rb +8 -8
  25. data/spec/spec_helper.rb +7 -7
  26. data/spec/support/chef/provisioning/fog_driver/providers/testdriver.rb +3 -3
  27. data/spec/unit/chef/provisioning/fog_driver/driver_spec.rb +39 -38
  28. data/spec/unit/fog_driver_spec.rb +6 -8
  29. data/spec/unit/providers/aws/credentials_spec.rb +10 -10
  30. data/spec/unit/providers/rackspace_spec.rb +5 -6
  31. data/spec/unit/providers/scaleway_spec.rb +9 -9
  32. data/spec/unit/providers/softlayer.rb +7 -7
  33. metadata +6 -36
  34. data/README.md +0 -357
@@ -1,3 +1,3 @@
1
- require 'chef/provisioning/fog_driver/driver'
1
+ require "chef/provisioning/fog_driver/driver"
2
2
 
3
3
  Chef::Provisioning.register_driver_class("fog", Chef::Provisioning::FogDriver::Driver)
@@ -1,763 +1,756 @@
1
- require 'chef/provisioning'
2
- require 'chef/provisioning/fog_driver/recipe_dsl'
3
-
4
- require 'chef/provisioning/driver'
5
- require 'chef/provisioning/machine/windows_machine'
6
- require 'chef/provisioning/machine/unix_machine'
7
- require 'chef/provisioning/machine_spec'
8
- require 'chef/provisioning/convergence_strategy/install_msi'
9
- require 'chef/provisioning/convergence_strategy/install_sh'
10
- require 'chef/provisioning/convergence_strategy/install_cached'
11
- require 'chef/provisioning/convergence_strategy/no_converge'
12
- require 'chef/provisioning/transport/ssh'
13
- require 'chef/provisioning/transport/winrm'
14
- require 'chef/provisioning/fog_driver/version'
15
-
16
- require 'fog'
17
- require 'fog/core'
18
- require 'fog/compute'
19
- require 'socket'
20
- require 'etc'
21
- require 'time'
22
- require 'retryable'
23
- require 'cheffish/merged_config'
24
- require 'chef/provisioning/fog_driver/recipe_dsl'
1
+ require "chef/provisioning"
2
+ require "chef/provisioning/fog_driver/recipe_dsl"
3
+
4
+ require "chef/provisioning/driver"
5
+ require "chef/provisioning/machine/windows_machine"
6
+ require "chef/provisioning/machine/unix_machine"
7
+ require "chef/provisioning/machine_spec"
8
+ require "chef/provisioning/convergence_strategy/install_msi"
9
+ require "chef/provisioning/convergence_strategy/install_sh"
10
+ require "chef/provisioning/convergence_strategy/install_cached"
11
+ require "chef/provisioning/convergence_strategy/no_converge"
12
+ require "chef/provisioning/transport/ssh"
13
+ require "chef/provisioning/transport/winrm"
14
+ require "chef/provisioning/fog_driver/version"
15
+
16
+ require "fog"
17
+ require "fog/core"
18
+ require "fog/compute"
19
+ require "socket"
20
+ require "etc"
21
+ require "time"
22
+ require "retryable"
23
+ require "cheffish/merged_config"
24
+ require "chef/provisioning/fog_driver/recipe_dsl"
25
25
 
26
26
  class Chef
27
- module Provisioning
28
- module FogDriver
29
- # Provisions cloud machines with the Fog driver.
30
- #
31
- # ## Fog Driver URLs
32
- #
33
- # All Chef Provisioning drivers use URLs to uniquely identify a driver's "bucket" of machines.
34
- # Fog URLs are of the form fog:<provider>:<identifier:> - see individual providers
35
- # for sample URLs.
36
- #
37
- # Identifier is generally something uniquely identifying the account. If multiple
38
- # users can access the account, the identifier should be the same for all of
39
- # them (do not use the username in these cases, use an account ID or auth server
40
- # URL).
41
- #
42
- # In particular, the identifier should be specific enough that if you create a
43
- # server with a driver with this URL, the server should be retrievable from
44
- # the same URL *no matter what else changes*. For example, an AWS account ID
45
- # is *not* enough for this--if you varied the region, you would no longer see
46
- # your server in the list. Thus, AWS uses both the account ID and the region.
47
- #
48
- # ## Supporting a new Fog provider
49
- #
50
- # The Fog driver does not immediately support all Fog providers out of the box.
51
- # Some minor work needs to be done to plug them into Chef.
52
- #
53
- # To add a new supported Fog provider, pick an appropriate identifier, go to
54
- # from_provider and compute_options_for, and add the new provider in the case
55
- # statements so that URLs for your Fog provider can be generated. If your
56
- # cloud provider has environment variables or standard config files (like
57
- # ~/.aws/credentials or ~/.aws/config), you can read those and merge that information
58
- # in the compute_options_for function.
59
- #
60
- # ## Reference format
61
- #
62
- # All machines have a reference hash to find them. These are the keys used by
63
- # the Fog provisioner:
64
- #
65
- # - driver_url: fog:<driver>:<unique_account_info>
66
- # - server_id: the ID of the server so it can be found again
67
- # - created_at: timestamp server was created
68
- # - started_at: timestamp server was last started
69
- # - is_windows, ssh_username, sudo: copied from machine_options
70
- #
71
- # ## Machine options
72
- #
73
- # Machine options (for allocation and readying the machine) include:
74
- #
75
- # - bootstrap_options: hash of options to pass to compute.servers.create
76
- # - is_windows: true if windows. TODO detect this from ami?
77
- # - create_timeout: the time to wait for the instance to boot to ssh (defaults to 180)
78
- # - start_timeout: the time to wait for the instance to start (defaults to 180)
79
- # - ssh_timeout: the time to wait for ssh to be available if the instance is detected as up (defaults to 20)
80
- # - ssh_username: username to use for ssh
81
- # - sudo: true to prefix all commands with "sudo"
82
- # - transport_address_location: ssh into machine via `:public_ip`, `:private_ip`, or `:ip_addresses`
83
- # - use_private_ip_for_ssh: (DEPRECATED and is replaced with `transport_address_location`) hint to use private floating_ip when available
84
- # - convergence_options: hash of options for the convergence strategy
85
- # - chef_client_timeout: the time to wait for chef-client to finish
86
- # - chef_server - the chef server to point convergence at
87
- #
88
- # Example bootstrap_options for ec2:
89
- #
90
- # :bootstrap_options => {
91
- # :image_id =>'ami-311f2b45',
92
- # :flavor_id =>'t1.micro',
93
- # :key_name => 'key-pair-name'
94
- # }
95
- #
96
- class Driver < Provisioning::Driver
97
- @@ip_pool_lock = Mutex.new
98
-
99
- include Chef::Mixin::ShellOut
100
-
101
- DEFAULT_OPTIONS = {
102
- :create_timeout => 180,
103
- :start_timeout => 180,
104
- :ssh_timeout => 20
105
- }
106
-
107
- RETRYABLE_ERRORS = [Fog::Compute::AWS::Error]
108
- RETRYABLE_OPTIONS = { tries: 12, sleep: 5, on: RETRYABLE_ERRORS }
109
-
110
- class << self
111
- alias :__new__ :new
112
-
113
- def inherited(klass)
114
- class << klass
115
- alias :new :__new__
27
+ module Provisioning
28
+ module FogDriver
29
+ # Provisions cloud machines with the Fog driver.
30
+ #
31
+ # ## Fog Driver URLs
32
+ #
33
+ # All Chef Provisioning drivers use URLs to uniquely identify a driver's "bucket" of machines.
34
+ # Fog URLs are of the form fog:<provider>:<identifier:> - see individual providers
35
+ # for sample URLs.
36
+ #
37
+ # Identifier is generally something uniquely identifying the account. If multiple
38
+ # users can access the account, the identifier should be the same for all of
39
+ # them (do not use the username in these cases, use an account ID or auth server
40
+ # URL).
41
+ #
42
+ # In particular, the identifier should be specific enough that if you create a
43
+ # server with a driver with this URL, the server should be retrievable from
44
+ # the same URL *no matter what else changes*. For example, an AWS account ID
45
+ # is *not* enough for this--if you varied the region, you would no longer see
46
+ # your server in the list. Thus, AWS uses both the account ID and the region.
47
+ #
48
+ # ## Supporting a new Fog provider
49
+ #
50
+ # The Fog driver does not immediately support all Fog providers out of the box.
51
+ # Some minor work needs to be done to plug them into Chef.
52
+ #
53
+ # To add a new supported Fog provider, pick an appropriate identifier, go to
54
+ # from_provider and compute_options_for, and add the new provider in the case
55
+ # statements so that URLs for your Fog provider can be generated. If your
56
+ # cloud provider has environment variables or standard config files (like
57
+ # ~/.aws/credentials or ~/.aws/config), you can read those and merge that information
58
+ # in the compute_options_for function.
59
+ #
60
+ # ## Reference format
61
+ #
62
+ # All machines have a reference hash to find them. These are the keys used by
63
+ # the Fog provisioner:
64
+ #
65
+ # - driver_url: fog:<driver>:<unique_account_info>
66
+ # - server_id: the ID of the server so it can be found again
67
+ # - created_at: timestamp server was created
68
+ # - started_at: timestamp server was last started
69
+ # - is_windows, ssh_username, sudo: copied from machine_options
70
+ #
71
+ # ## Machine options
72
+ #
73
+ # Machine options (for allocation and readying the machine) include:
74
+ #
75
+ # - bootstrap_options: hash of options to pass to compute.servers.create
76
+ # - is_windows: true if windows. TODO detect this from ami?
77
+ # - create_timeout: the time to wait for the instance to boot to ssh (defaults to 180)
78
+ # - start_timeout: the time to wait for the instance to start (defaults to 180)
79
+ # - ssh_timeout: the time to wait for ssh to be available if the instance is detected as up (defaults to 20)
80
+ # - ssh_username: username to use for ssh
81
+ # - sudo: true to prefix all commands with "sudo"
82
+ # - transport_address_location: ssh into machine via `:public_ip`, `:private_ip`, or `:ip_addresses`
83
+ # - use_private_ip_for_ssh: (DEPRECATED and is replaced with `transport_address_location`) hint to use private floating_ip when available
84
+ # - convergence_options: hash of options for the convergence strategy
85
+ # - chef_client_timeout: the time to wait for chef-client to finish
86
+ # - chef_server - the chef server to point convergence at
87
+ #
88
+ # Example bootstrap_options for ec2:
89
+ #
90
+ # :bootstrap_options => {
91
+ # :image_id =>'ami-311f2b45',
92
+ # :flavor_id =>'t1.micro',
93
+ # :key_name => 'key-pair-name'
94
+ # }
95
+ #
96
+ class Driver < Provisioning::Driver
97
+ @@ip_pool_lock = Mutex.new
98
+
99
+ include Chef::Mixin::ShellOut
100
+
101
+ DEFAULT_OPTIONS = {
102
+ create_timeout: 180,
103
+ start_timeout: 180,
104
+ ssh_timeout: 20
105
+ }.freeze
106
+
107
+ RETRYABLE_ERRORS = [Fog::Compute::AWS::Error].freeze
108
+ RETRYABLE_OPTIONS = { tries: 12, sleep: 5, on: RETRYABLE_ERRORS }.freeze
109
+
110
+ class << self
111
+ alias __new__ new
112
+
113
+ def inherited(klass)
114
+ class << klass
115
+ alias_method :new, :__new__
116
+ end
117
+ end
116
118
  end
117
- end
118
- end
119
119
 
120
- @@registered_provider_classes = {}
121
- def self.register_provider_class(name, driver)
122
- @@registered_provider_classes[name] = driver
123
- end
120
+ @@registered_provider_classes = {}
121
+ def self.register_provider_class(name, driver)
122
+ @@registered_provider_classes[name] = driver
123
+ end
124
124
 
125
- def self.provider_class_for(provider)
126
- require "chef/provisioning/fog_driver/providers/#{provider.downcase}"
127
- @@registered_provider_classes[provider]
128
- end
125
+ def self.provider_class_for(provider)
126
+ require "chef/provisioning/fog_driver/providers/#{provider.downcase}"
127
+ @@registered_provider_classes[provider]
128
+ end
129
129
 
130
- def self.new(driver_url, config)
131
- provider = driver_url.split(':')[1]
132
- provider_class_for(provider).new(driver_url, config)
133
- end
130
+ def self.new(driver_url, config)
131
+ provider = driver_url.split(":")[1]
132
+ provider_class_for(provider).new(driver_url, config)
133
+ end
134
134
 
135
- # Passed in a driver_url, and a config in the format of Driver.config.
136
- def self.from_url(driver_url, config)
137
- Driver.new(driver_url, config)
138
- end
135
+ # Passed in a driver_url, and a config in the format of Driver.config.
136
+ def self.from_url(driver_url, config)
137
+ Driver.new(driver_url, config)
138
+ end
139
139
 
140
- def self.canonicalize_url(driver_url, config)
141
- _, provider, id = driver_url.split(':', 3)
142
- config, id = provider_class_for(provider).compute_options_for(provider, id, config)
143
- [ "fog:#{provider}:#{id}", config ]
144
- end
140
+ def self.canonicalize_url(driver_url, config)
141
+ _, provider, id = driver_url.split(":", 3)
142
+ config, id = provider_class_for(provider).compute_options_for(provider, id, config)
143
+ ["fog:#{provider}:#{id}", config]
144
+ end
145
145
 
146
- # Passed in a config which is *not* merged with driver_url (because we don't
147
- # know what it is yet) but which has the same keys
148
- def self.from_provider(provider, config)
149
- # Figure out the options and merge them into the config
150
- config, id = provider_class_for(provider).compute_options_for(provider, nil, config)
146
+ # Passed in a config which is *not* merged with driver_url (because we don't
147
+ # know what it is yet) but which has the same keys
148
+ def self.from_provider(provider, config)
149
+ # Figure out the options and merge them into the config
150
+ config, id = provider_class_for(provider).compute_options_for(provider, nil, config)
151
151
 
152
- driver_url = "fog:#{provider}:#{id}"
152
+ driver_url = "fog:#{provider}:#{id}"
153
153
 
154
- Provisioning.driver_for_url(driver_url, config)
155
- end
154
+ Provisioning.driver_for_url(driver_url, config)
155
+ end
156
156
 
157
- # Create a new Fog driver.
158
- #
159
- # ## Parameters
160
- # driver_url - URL of driver. "fog:<provider>:<provider_id>"
161
- # config - configuration. :driver_options, :keys, :key_paths and :log_level are used.
162
- # driver_options is a hash with these possible options:
163
- # - compute_options: the hash of options to Fog::Compute.new.
164
- # - aws_config_file: aws config file (defaults: ~/.aws/credentials, ~/.aws/config)
165
- # - aws_csv_file: aws csv credentials file downloaded from EC2 interface
166
- # - aws_profile: profile name to use for credentials
167
- # - aws_credentials: AWSCredentials object. (will be created for you by default)
168
- # - log_level: :debug, :info, :warn, :error
169
- def initialize(driver_url, config)
170
- super(driver_url, config)
171
- if config[:log_level] == :debug
172
- Fog::Logger[:debug] = ::STDERR
173
- Excon.defaults[:debug_request] = true
174
- Excon.defaults[:debug_response] = true
175
- end
176
- end
157
+ # Create a new Fog driver.
158
+ #
159
+ # ## Parameters
160
+ # driver_url - URL of driver. "fog:<provider>:<provider_id>"
161
+ # config - configuration. :driver_options, :keys, :key_paths and :log_level are used.
162
+ # driver_options is a hash with these possible options:
163
+ # - compute_options: the hash of options to Fog::Compute.new.
164
+ # - aws_config_file: aws config file (defaults: ~/.aws/credentials, ~/.aws/config)
165
+ # - aws_csv_file: aws csv credentials file downloaded from EC2 interface
166
+ # - aws_profile: profile name to use for credentials
167
+ # - aws_credentials: AWSCredentials object. (will be created for you by default)
168
+ # - log_level: :debug, :info, :warn, :error
169
+ def initialize(driver_url, config)
170
+ super(driver_url, config)
171
+ if config[:log_level] == :debug
172
+ Fog::Logger[:debug] = ::STDERR
173
+ Excon.defaults[:debug_request] = true
174
+ Excon.defaults[:debug_response] = true
175
+ end
176
+ end
177
177
 
178
- def compute_options
179
- JSON.parse(driver_options[:compute_options].to_h.to_json, symbolize_names: true) || {}
180
- end
178
+ def compute_options
179
+ JSON.parse(driver_options[:compute_options].to_h.to_json, symbolize_names: true) || {}
180
+ end
181
181
 
182
- def provider
183
- compute_options[:provider]
184
- end
182
+ def provider
183
+ compute_options[:provider]
184
+ end
185
185
 
186
- # Acquire a machine, generally by provisioning it. Returns a Machine
187
- # object pointing at the machine, allowing useful actions like setup,
188
- # converge, execute, file and directory.
189
- def allocate_machine(action_handler, machine_spec, machine_options)
190
- # If the server does not exist, create it
191
- create_servers(action_handler, { machine_spec => machine_options }, Chef::ChefFS::Parallelizer.new(0))
192
- machine_spec
193
- end
186
+ # Acquire a machine, generally by provisioning it. Returns a Machine
187
+ # object pointing at the machine, allowing useful actions like setup,
188
+ # converge, execute, file and directory.
189
+ def allocate_machine(action_handler, machine_spec, machine_options)
190
+ # If the server does not exist, create it
191
+ create_servers(action_handler, { machine_spec => machine_options }, Chef::ChefFS::Parallelizer.new(0))
192
+ machine_spec
193
+ end
194
194
 
195
- def allocate_machines(action_handler, specs_and_options, parallelizer)
196
- create_servers(action_handler, specs_and_options, parallelizer) do |machine_spec, server|
197
- yield machine_spec
198
- end
199
- specs_and_options.keys
200
- end
195
+ def allocate_machines(action_handler, specs_and_options, parallelizer)
196
+ create_servers(action_handler, specs_and_options, parallelizer) do |machine_spec, _server|
197
+ yield machine_spec
198
+ end
199
+ specs_and_options.keys
200
+ end
201
201
 
202
- def ready_machine(action_handler, machine_spec, machine_options)
203
- server = server_for(machine_spec)
204
- if server.nil?
205
- raise "Machine #{machine_spec.name} does not have a server associated with it, or server does not exist."
206
- end
202
+ def ready_machine(action_handler, machine_spec, machine_options)
203
+ server = server_for(machine_spec)
204
+ if server.nil?
205
+ raise "Machine #{machine_spec.name} does not have a server associated with it, or server does not exist."
206
+ end
207
207
 
208
- # Start the server if needed, and wait for it to start
209
- start_server(action_handler, machine_spec, server)
210
- wait_until_ready(action_handler, machine_spec, machine_options, server)
211
-
212
- converge_floating_ips(action_handler, machine_spec, machine_options, server)
213
-
214
- begin
215
- wait_for_transport(action_handler, machine_spec, machine_options, server)
216
- rescue Fog::Errors::TimeoutError
217
- # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting
218
- if machine_spec.reference['started_at'] || remaining_wait_time(machine_spec, machine_options) < -(10*60)
219
- raise
220
- else
221
- # Sometimes (on EC2) the machine comes up but gets stuck or has
222
- # some other problem. If this is the case, we restart the server
223
- # to unstick it. Reboot covers a multitude of sins.
224
- Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) was started but SSH did not come up. Rebooting machine in an attempt to unstick it ..."
225
- restart_server(action_handler, machine_spec, server)
208
+ # Start the server if needed, and wait for it to start
209
+ start_server(action_handler, machine_spec, server)
226
210
  wait_until_ready(action_handler, machine_spec, machine_options, server)
227
- wait_for_transport(action_handler, machine_spec, machine_options, server)
228
- end
229
- end
230
211
 
231
- machine_for(machine_spec, machine_options, server)
232
- end
233
-
234
- # Connect to machine without acquiring it
235
- def connect_to_machine(machine_spec, machine_options)
236
- machine_for(machine_spec, machine_options)
237
- end
212
+ converge_floating_ips(action_handler, machine_spec, machine_options, server)
213
+
214
+ begin
215
+ wait_for_transport(action_handler, machine_spec, machine_options, server)
216
+ rescue Fog::Errors::TimeoutError
217
+ # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting
218
+ if machine_spec.reference["started_at"] || remaining_wait_time(machine_spec, machine_options) < -(10 * 60)
219
+ raise
220
+ else
221
+ # Sometimes (on EC2) the machine comes up but gets stuck or has
222
+ # some other problem. If this is the case, we restart the server
223
+ # to unstick it. Reboot covers a multitude of sins.
224
+ Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) was started but SSH did not come up. Rebooting machine in an attempt to unstick it ..."
225
+ restart_server(action_handler, machine_spec, server)
226
+ wait_until_ready(action_handler, machine_spec, machine_options, server)
227
+ wait_for_transport(action_handler, machine_spec, machine_options, server)
228
+ end
229
+ end
238
230
 
239
- def destroy_machine(action_handler, machine_spec, machine_options)
240
- server = server_for(machine_spec)
241
- if server
242
- action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.reference['server_id']} at #{driver_url})" do
243
- server.destroy
244
- machine_spec.reference = nil
231
+ machine_for(machine_spec, machine_options, server)
245
232
  end
246
- end
247
- strategy = ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
248
- strategy.cleanup_convergence(action_handler, machine_spec)
249
- end
250
233
 
251
- def stop_machine(action_handler, machine_spec, machine_options)
252
- server = server_for(machine_spec)
253
- if server
254
- action_handler.perform_action "stop machine #{machine_spec.name} (#{server.id} at #{driver_url})" do
255
- server.stop
234
+ # Connect to machine without acquiring it
235
+ def connect_to_machine(machine_spec, machine_options)
236
+ machine_for(machine_spec, machine_options)
256
237
  end
257
- end
258
- end
259
238
 
260
- def image_for(image_spec)
261
- compute.images.get(image_spec.reference['image_id'])
262
- end
263
-
264
- def compute
265
- @compute ||= Fog::Compute.new(compute_options)
266
- end
239
+ def destroy_machine(action_handler, machine_spec, machine_options)
240
+ server = server_for(machine_spec)
241
+ if server
242
+ action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.reference['server_id']} at #{driver_url})" do
243
+ server.destroy
244
+ machine_spec.reference = nil
245
+ end
246
+ end
247
+ strategy = ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
248
+ strategy.cleanup_convergence(action_handler, machine_spec)
249
+ end
267
250
 
268
- # Not meant to be part of public interface
269
- def transport_for(machine_spec, machine_options, server, action_handler = nil)
270
- Chef::Log.debug("Creating transport for #{server}")
271
- Chef::Log.debug("Machine Spec: #{machine_spec}")
272
- if machine_spec.reference['is_windows']
273
- action_handler.report_progress "Waiting for admin password on #{machine_spec.name} to be ready (may take up to 15 minutes)..." if action_handler
274
- transport = create_winrm_transport(machine_spec, machine_options, server)
275
- action_handler.report_progress 'Admin password available ...' if action_handler
276
- transport
277
- else
278
- create_ssh_transport(machine_spec, machine_options, server)
279
- end
280
- end
251
+ def stop_machine(action_handler, machine_spec, _machine_options)
252
+ server = server_for(machine_spec)
253
+ if server
254
+ action_handler.perform_action "stop machine #{machine_spec.name} (#{server.id} at #{driver_url})" do
255
+ server.stop
256
+ end
257
+ end
258
+ end
281
259
 
282
- def create_volume(action_handler, volume_spec, volume_options)
283
- raise "##create_volume not implemented in in #{self.class}"
284
- end
260
+ def image_for(image_spec)
261
+ compute.images.get(image_spec.reference["image_id"])
262
+ end
285
263
 
286
- def destroy_volume(action_handler, volume_spec, volume_options)
287
- raise "##destroy_volume not implemented in in #{self.class}"
288
- end
264
+ def compute
265
+ @compute ||= Fog::Compute.new(compute_options)
266
+ end
289
267
 
290
- protected
268
+ # Not meant to be part of public interface
269
+ def transport_for(machine_spec, machine_options, server, action_handler = nil)
270
+ Chef::Log.debug("Creating transport for #{server}")
271
+ Chef::Log.debug("Machine Spec: #{machine_spec}")
272
+ if machine_spec.reference["is_windows"]
273
+ action_handler.report_progress "Waiting for admin password on #{machine_spec.name} to be ready (may take up to 15 minutes)..." if action_handler
274
+ transport = create_winrm_transport(machine_spec, machine_options, server)
275
+ action_handler.report_progress "Admin password available ..." if action_handler
276
+ transport
277
+ else
278
+ create_ssh_transport(machine_spec, machine_options, server)
279
+ end
280
+ end
291
281
 
292
- def option_for(machine_options, key)
293
- machine_options[:bootstrap_options][key] || DEFAULT_OPTIONS[key]
294
- end
282
+ def create_volume(_action_handler, _volume_spec, _volume_options)
283
+ raise "##create_volume not implemented in in #{self.class}"
284
+ end
295
285
 
296
- def creator
297
- raise "unsupported Fog provider #{provider} (please implement #creator)"
298
- end
286
+ def destroy_volume(_action_handler, _volume_spec, _volume_options)
287
+ raise "##destroy_volume not implemented in in #{self.class}"
288
+ end
299
289
 
300
- def create_servers(action_handler, specs_and_options, parallelizer, &block)
301
- specs_and_servers = servers_for(specs_and_options)
290
+ protected
302
291
 
303
- # Get the list of servers which exist, segmented by their bootstrap options
304
- # (we will try to create a set of servers for each set of bootstrap options
305
- # with create_many)
306
- by_bootstrap_options = {}
307
- specs_and_options.each do |machine_spec, machine_options|
308
- server = specs_and_servers[machine_spec]
309
- if server
310
- server_state = server.respond_to?(:status) ? server.status : server.state
311
- if %w(terminated archive DELETED).include?(server_state.downcase) # Can't come back from that
312
- Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) is terminated. Recreating ..."
313
- else
314
- yield machine_spec, server if block_given?
315
- next
316
- end
317
- elsif machine_spec.reference
318
- Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.reference['server_id']} on #{driver_url}) no longer exists. Recreating ..."
292
+ def option_for(machine_options, key)
293
+ machine_options[:bootstrap_options][key] || DEFAULT_OPTIONS[key]
319
294
  end
320
295
 
321
- machine_spec.reference ||= {}
322
- machine_spec.reference.update(
323
- 'driver_url' => driver_url,
324
- 'driver_version' => FogDriver::VERSION,
325
- 'creator' => creator,
326
- 'allocated_at' => Time.now.to_i
327
- )
296
+ def creator
297
+ raise "unsupported Fog provider #{provider} (please implement #creator)"
298
+ end
328
299
 
329
- bootstrap_options = bootstrap_options_for(machine_spec, machine_options)
330
- machine_spec.reference['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
331
- by_bootstrap_options[bootstrap_options] ||= []
332
- by_bootstrap_options[bootstrap_options] << machine_spec
300
+ def create_servers(action_handler, specs_and_options, parallelizer)
301
+ specs_and_servers = servers_for(specs_and_options)
302
+
303
+ # Get the list of servers which exist, segmented by their bootstrap options
304
+ # (we will try to create a set of servers for each set of bootstrap options
305
+ # with create_many)
306
+ by_bootstrap_options = {}
307
+ specs_and_options.each do |machine_spec, machine_options|
308
+ server = specs_and_servers[machine_spec]
309
+ if server
310
+ server_state = server.respond_to?(:status) ? server.status : server.state
311
+ if %w{terminated archive DELETED}.include?(server_state.downcase) # Can't come back from that
312
+ Chef::Log.warn "Machine #{machine_spec.name} (#{server.id} on #{driver_url}) is terminated. Recreating ..."
313
+ else
314
+ yield machine_spec, server if block_given?
315
+ next
316
+ end
317
+ elsif machine_spec.reference
318
+ Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.reference['server_id']} on #{driver_url}) no longer exists. Recreating ..."
319
+ end
333
320
 
334
- # TODO 2.0 We no longer support `use_private_ip_for_ssh`, only `transport_address_location
335
- if machine_options[:use_private_ip_for_ssh]
336
- unless @transport_address_location_warned
321
+ machine_spec.reference ||= {}
322
+ machine_spec.reference.update(
323
+ "driver_url" => driver_url,
324
+ "driver_version" => FogDriver::VERSION,
325
+ "creator" => creator,
326
+ "allocated_at" => Time.now.to_i
327
+ )
328
+
329
+ bootstrap_options = bootstrap_options_for(machine_spec, machine_options)
330
+ machine_spec.reference["key_name"] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
331
+ by_bootstrap_options[bootstrap_options] ||= []
332
+ by_bootstrap_options[bootstrap_options] << machine_spec
333
+
334
+ # TODO: 2.0 We no longer support `use_private_ip_for_ssh`, only `transport_address_location
335
+ if machine_options[:use_private_ip_for_ssh]
336
+ unless @transport_address_location_warned
337
337
  Chef::Log.warn("The machine option ':use_private_ip_for_ssh' has been deprecated, use ':transport_address_location'")
338
338
  @transport_address_location_warned = true
339
+ end
340
+ machine_options = Cheffish::MergedConfig.new(machine_options, transport_address_location: :private_ip)
339
341
  end
340
- machine_options = Cheffish::MergedConfig.new(machine_options, {:transport_address_location => :private_ip})
341
- end
342
- %w(is_windows ssh_username sudo transport_address_location ssh_gateway).each do |key|
343
- machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
344
- end
345
- end
342
+ %w{is_windows ssh_username sudo transport_address_location ssh_gateway}.each do |key|
343
+ machine_spec.reference[key] = machine_options[key.to_sym] if machine_options[key.to_sym]
344
+ end
345
+ end
346
346
 
347
- # Create the servers in parallel
348
- parallelizer.parallelize(by_bootstrap_options) do |bootstrap_options, machine_specs|
349
- machine_description = if machine_specs.size == 1
350
- "machine #{machine_specs.first.name}"
351
- else
352
- "machines #{machine_specs.map { |s| s.name }.join(", ")}"
347
+ # Create the servers in parallel
348
+ parallelizer.parallelize(by_bootstrap_options) do |bootstrap_options, machine_specs|
349
+ machine_description = if machine_specs.size == 1
350
+ "machine #{machine_specs.first.name}"
351
+ else
352
+ "machines #{machine_specs.map(&:name).join(', ')}"
353
+ end
354
+ description = ["creating #{machine_description} on #{driver_url}"]
355
+ bootstrap_options.each_pair { |key, value| description << " #{key}: #{value.inspect}" }
356
+ action_handler.report_progress description
357
+ if action_handler.should_perform_actions
358
+ # Actually create the servers
359
+ create_many_servers(machine_specs.size, bootstrap_options, parallelizer) do |server|
360
+ # Assign each one to a machine spec
361
+ machine_spec = machine_specs.pop
362
+ machine_options = specs_and_options[machine_spec]
363
+ machine_spec.reference["server_id"] = server.id
364
+
365
+ action_handler.performed_action "machine #{machine_spec.name} created as #{server.id} on #{driver_url}"
366
+
367
+ yield machine_spec, server if block_given?
368
+ end
369
+
370
+ unless machine_specs.empty?
371
+ raise "Not all machines were created by create_many_servers!"
372
+ end
373
+ end
374
+ end.to_a
353
375
  end
354
- description = [ "creating #{machine_description} on #{driver_url}" ]
355
- bootstrap_options.each_pair { |key,value| description << " #{key}: #{value.inspect}" }
356
- action_handler.report_progress description
357
- if action_handler.should_perform_actions
358
- # Actually create the servers
359
- create_many_servers(machine_specs.size, bootstrap_options, parallelizer) do |server|
360
376
 
361
- # Assign each one to a machine spec
362
- machine_spec = machine_specs.pop
363
- machine_options = specs_and_options[machine_spec]
364
- machine_spec.reference['server_id'] = server.id
377
+ def create_many_servers(num_servers, bootstrap_options, parallelizer)
378
+ parallelizer.parallelize(1.upto(num_servers)) do |_i|
379
+ clean_bootstrap_options = Marshal.load(Marshal.dump(bootstrap_options)) # Prevent destructive operations on bootstrap_options.
365
380
 
366
- action_handler.performed_action "machine #{machine_spec.name} created as #{server.id} on #{driver_url}"
381
+ server = compute.servers.create(clean_bootstrap_options)
382
+ yield server if block_given?
383
+ server
384
+ end.to_a
385
+ end
367
386
 
368
- yield machine_spec, server if block_given?
387
+ def start_server(action_handler, machine_spec, server)
388
+ # If it is stopping, wait for it to get out of "stopping" transition status before starting
389
+ server_state = server.respond_to?(:status) ? server.status : server.state
390
+ if server_state == "stopping"
391
+ action_handler.report_progress "wait for #{machine_spec.name} (#{server.id} on #{driver_url}) to finish stopping ..."
392
+ server.wait_for { server_state != "stopping" }
393
+ action_handler.report_progress "#{machine_spec.name} is now stopped"
369
394
  end
370
-
371
- if machine_specs.size > 0
372
- raise "Not all machines were created by create_many_servers!"
395
+ if server_state == "stopped"
396
+ action_handler.perform_action "start machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
397
+ server.start
398
+ machine_spec.reference["started_at"] = Time.now.to_i
399
+ end
400
+ machine_spec.save(action_handler)
373
401
  end
374
402
  end
375
- end.to_a
376
- end
377
403
 
378
- def create_many_servers(num_servers, bootstrap_options, parallelizer)
379
- parallelizer.parallelize(1.upto(num_servers)) do |i|
380
- clean_bootstrap_options = Marshal.load(Marshal.dump(bootstrap_options)) # Prevent destructive operations on bootstrap_options.
404
+ def restart_server(action_handler, machine_spec, server)
405
+ action_handler.perform_action "restart machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
406
+ server.reboot
407
+ machine_spec.reference["started_at"] = Time.now.to_i
408
+ end
409
+ machine_spec.save(action_handler)
410
+ end
381
411
 
382
- server = compute.servers.create(clean_bootstrap_options)
383
- yield server if block_given?
384
- server
385
- end.to_a
386
- end
412
+ def remaining_wait_time(machine_spec, machine_options)
413
+ if machine_spec.reference["started_at"]
414
+ timeout = option_for(machine_options, :start_timeout) - (Time.now.utc - parse_time(machine_spec.reference["started_at"]))
415
+ else
416
+ timeout = option_for(machine_options, :create_timeout) - (Time.now.utc - parse_time(machine_spec.reference["allocated_at"]))
417
+ end
418
+ timeout > 0 ? timeout : 0.01
419
+ end
387
420
 
388
- def start_server(action_handler, machine_spec, server)
389
- # If it is stopping, wait for it to get out of "stopping" transition status before starting
390
- server_state = server.respond_to?(:status) ? server.status : server.state
391
- if server_state == 'stopping'
392
- action_handler.report_progress "wait for #{machine_spec.name} (#{server.id} on #{driver_url}) to finish stopping ..."
393
- server.wait_for { server_state != 'stopping' }
394
- action_handler.report_progress "#{machine_spec.name} is now stopped"
395
- end
396
- if server_state == 'stopped'
397
- action_handler.perform_action "start machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
398
- server.start
399
- machine_spec.reference['started_at'] = Time.now.to_i
421
+ def parse_time(value)
422
+ if value.is_a?(String)
423
+ Time.parse(value)
424
+ else
425
+ Time.at(value)
426
+ end
400
427
  end
401
- machine_spec.save(action_handler)
402
- end
403
- end
404
428
 
405
- def restart_server(action_handler, machine_spec, server)
406
- action_handler.perform_action "restart machine #{machine_spec.name} (#{server.id} on #{driver_url})" do
407
- server.reboot
408
- machine_spec.reference['started_at'] = Time.now.to_i
409
- end
410
- machine_spec.save(action_handler)
411
- end
429
+ def wait_until_ready(action_handler, machine_spec, machine_options, server)
430
+ unless server.ready?
431
+ if action_handler.should_perform_actions
432
+ Retryable.retryable(RETRYABLE_OPTIONS) do |retries, _exception|
433
+ action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be ready, API attempt #{retries + 1}/#{RETRYABLE_OPTIONS[:tries]} ..."
434
+ server.wait_for(remaining_wait_time(machine_spec, machine_options)) { ready? }
435
+ end
436
+ action_handler.report_progress "#{machine_spec.name} is now ready"
437
+ end
438
+ end
439
+ end
412
440
 
413
- def remaining_wait_time(machine_spec, machine_options)
414
- if machine_spec.reference['started_at']
415
- timeout = option_for(machine_options, :start_timeout) - (Time.now.utc - parse_time(machine_spec.reference['started_at']))
416
- else
417
- timeout = option_for(machine_options, :create_timeout) - (Time.now.utc - parse_time(machine_spec.reference['allocated_at']))
418
- end
419
- timeout > 0 ? timeout : 0.01
420
- end
441
+ def wait_for_transport(action_handler, machine_spec, machine_options, server)
442
+ transport = transport_for(machine_spec, machine_options, server, action_handler)
443
+ unless transport.available?
444
+ if action_handler.should_perform_actions
445
+ Retryable.retryable(RETRYABLE_OPTIONS) do |retries, _exception|
446
+ action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be connectable (transport up and running), API attempt #{retries + 1}/#{RETRYABLE_OPTIONS[:tries]} ..."
421
447
 
422
- def parse_time(value)
423
- if value.is_a?(String)
424
- Time.parse(value)
425
- else
426
- Time.at(value)
427
- end
428
- end
448
+ _self = self
429
449
 
430
- def wait_until_ready(action_handler, machine_spec, machine_options, server)
431
- if !server.ready?
432
- if action_handler.should_perform_actions
433
- Retryable.retryable(RETRYABLE_OPTIONS) do |retries,exception|
434
- action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be ready, API attempt #{retries+1}/#{RETRYABLE_OPTIONS[:tries]} ..."
435
- server.wait_for(remaining_wait_time(machine_spec, machine_options)) { ready? }
450
+ server.wait_for(remaining_wait_time(machine_spec, machine_options)) do
451
+ transport.available?
452
+ end
453
+ end
454
+ action_handler.report_progress "#{machine_spec.name} is now connectable"
455
+ end
436
456
  end
437
- action_handler.report_progress "#{machine_spec.name} is now ready"
438
457
  end
439
- end
440
- end
441
458
 
442
- def wait_for_transport(action_handler, machine_spec, machine_options, server)
459
+ def converge_floating_ips(action_handler, _machine_spec, machine_options, server)
460
+ pool = option_for(machine_options, :floating_ip_pool)
461
+ floating_ip = option_for(machine_options, :floating_ip)
462
+ attached_floating_ips = find_floating_ips(server, action_handler)
463
+ if pool
464
+
465
+ Chef::Log.debug "Attaching IP from pool #{pool}"
466
+ if !attached_floating_ips.empty?
467
+ Chef::Log.info "Server already assigned attached_floating_ips `#{attached_floating_ips}`"
468
+ elsif
469
+ action_handler.perform_action "Attaching floating IP from pool `#{pool}`" do
470
+ attach_ip_from_pool(server, pool)
471
+ end
472
+ end
473
+
474
+ elsif floating_ip
443
475
 
444
- transport = transport_for(machine_spec, machine_options, server, action_handler)
445
- if !transport.available?
446
- if action_handler.should_perform_actions
447
- Retryable.retryable(RETRYABLE_OPTIONS) do |retries,exception|
448
- action_handler.report_progress "waiting for #{machine_spec.name} (#{server.id} on #{driver_url}) to be connectable (transport up and running), API attempt #{retries+1}/#{RETRYABLE_OPTIONS[:tries]} ..."
476
+ Chef::Log.debug "Attaching given IP #{floating_ip}"
477
+ if attached_floating_ips.include? floating_ip
478
+ Chef::Log.info "Address <#{floating_ip}> already allocated"
479
+ else
480
+ action_handler.perform_action "Attaching floating IP #{floating_ip}" do
481
+ attach_ip(server, floating_ip)
482
+ end
483
+ end
449
484
 
450
- _self = self
485
+ elsif !attached_floating_ips.empty?
451
486
 
452
- server.wait_for(remaining_wait_time(machine_spec, machine_options)) do
453
- transport.available?
487
+ # If nothing is assigned, lets remove any floating IPs
488
+ Chef::Log.debug "Missing :floating_ip_pool or :floating_ip, removing attached floating IPs"
489
+ action_handler.perform_action "Removing floating IPs #{attached_floating_ips}" do
490
+ attached_floating_ips.each do |ip|
491
+ server.disassociate_address(ip)
492
+ end
493
+ server.reload
454
494
  end
455
495
  end
456
- action_handler.report_progress "#{machine_spec.name} is now connectable"
457
496
  end
458
- end
459
- end
460
497
 
461
- def converge_floating_ips(action_handler, machine_spec, machine_options, server)
462
- pool = option_for(machine_options, :floating_ip_pool)
463
- floating_ip = option_for(machine_options, :floating_ip)
464
- attached_floating_ips = find_floating_ips(server, action_handler)
465
- if pool
466
-
467
- Chef::Log.debug "Attaching IP from pool #{pool}"
468
- if attached_floating_ips.size > 0
469
- Chef::Log.info "Server already assigned attached_floating_ips `#{attached_floating_ips}`"
470
- elsif
471
- action_handler.perform_action "Attaching floating IP from pool `#{pool}`" do
472
- attach_ip_from_pool(server, pool)
498
+ # Find all attached floating IPs from all networks
499
+ def find_floating_ips(server, action_handler)
500
+ floating_ips = []
501
+ Retryable.retryable(RETRYABLE_OPTIONS) do |retries, _exception|
502
+ action_handler.report_progress "Querying for floating IPs attached to server #{server.id}, API attempt #{retries + 1}/#{RETRYABLE_OPTIONS[:tries]} ..."
503
+ server.addresses.each do |_network, addrs|
504
+ addrs.each do |full_addr|
505
+ if full_addr["OS-EXT-IPS:type"] == "floating"
506
+ floating_ips << full_addr["addr"]
507
+ end
508
+ end
509
+ end
473
510
  end
511
+ floating_ips
474
512
  end
475
513
 
476
- elsif floating_ip
477
-
478
- Chef::Log.debug "Attaching given IP #{floating_ip}"
479
- if attached_floating_ips.include? floating_ip
480
- Chef::Log.info "Address <#{floating_ip}> already allocated"
481
- else
482
- action_handler.perform_action "Attaching floating IP #{floating_ip}" do
483
- attach_ip(server, floating_ip)
514
+ # Attach IP to machine from IP pool
515
+ # Code taken from kitchen-openstack driver
516
+ # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb
517
+ def attach_ip_from_pool(server, pool)
518
+ @@ip_pool_lock.synchronize do
519
+ Chef::Log.info "Attaching floating IP from <#{pool}> pool"
520
+ free_addrs = compute.addresses.map do |i|
521
+ i.ip if i.fixed_ip.nil? && i.instance_id.nil? && i.pool == pool
522
+ end.compact
523
+ raise "No available IPs in pool <#{pool}>" if free_addrs.empty?
524
+ attach_ip(server, free_addrs[0])
484
525
  end
485
526
  end
486
527
 
487
- elsif !attached_floating_ips.empty?
488
-
489
- # If nothing is assigned, lets remove any floating IPs
490
- Chef::Log.debug 'Missing :floating_ip_pool or :floating_ip, removing attached floating IPs'
491
- action_handler.perform_action "Removing floating IPs #{attached_floating_ips}" do
492
- attached_floating_ips.each do |ip|
493
- server.disassociate_address(ip)
494
- end
528
+ # Attach given IP to machine, assign it as public
529
+ # Code taken from kitchen-openstack driver
530
+ # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb
531
+ def attach_ip(server, ip)
532
+ Chef::Log.info "Attaching floating IP <#{ip}>"
533
+ server.associate_address ip
495
534
  server.reload
496
535
  end
497
- end
498
- end
499
536
 
500
- # Find all attached floating IPs from all networks
501
- def find_floating_ips(server, action_handler)
502
- floating_ips = []
503
- Retryable.retryable(RETRYABLE_OPTIONS) do |retries,exception|
504
- action_handler.report_progress "Querying for floating IPs attached to server #{server.id}, API attempt #{retries+1}/#{RETRYABLE_OPTIONS[:tries]} ..."
505
- server.addresses.each do |network, addrs|
506
- addrs.each do | full_addr |
507
- if full_addr['OS-EXT-IPS:type'] == 'floating'
508
- floating_ips << full_addr['addr']
509
- end
537
+ def symbolize_keys(options)
538
+ options.each_with_object({}) do |(key, value), result|
539
+ result[key.to_sym] = value
510
540
  end
511
541
  end
512
- end
513
- floating_ips
514
- end
515
542
 
516
- # Attach IP to machine from IP pool
517
- # Code taken from kitchen-openstack driver
518
- # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb
519
- def attach_ip_from_pool(server, pool)
520
- @@ip_pool_lock.synchronize do
521
- Chef::Log.info "Attaching floating IP from <#{pool}> pool"
522
- free_addrs = compute.addresses.map do |i|
523
- i.ip if i.fixed_ip.nil? && i.instance_id.nil? && i.pool == pool
524
- end.compact
525
- if free_addrs.empty?
526
- raise RuntimeError, "No available IPs in pool <#{pool}>"
527
- end
528
- attach_ip(server, free_addrs[0])
529
- end
530
- end
531
-
532
- # Attach given IP to machine, assign it as public
533
- # Code taken from kitchen-openstack driver
534
- # https://github.com/test-kitchen/kitchen-openstack/blob/master/lib/kitchen/driver/openstack.rb
535
- def attach_ip(server, ip)
536
- Chef::Log.info "Attaching floating IP <#{ip}>"
537
- server.associate_address ip
538
- server.reload
539
- end
540
-
541
- def symbolize_keys(options)
542
- options.inject({}) do |result,(key,value)|
543
- result[key.to_sym] = value
544
- result
545
- end
546
- end
547
-
548
- def server_for(machine_spec)
549
- if machine_spec.reference
550
- compute.servers.get(machine_spec.reference['server_id'])
551
- else
552
- nil
553
- end
554
- end
543
+ def server_for(machine_spec)
544
+ if machine_spec.reference
545
+ compute.servers.get(machine_spec.reference["server_id"])
546
+ end
547
+ end
555
548
 
556
- def servers_for(specs_and_options)
557
- result = {}
558
- specs_and_options.each do |machine_spec, _machine_options|
559
- if machine_spec.reference
560
- if machine_spec.reference['driver_url'] != driver_url
561
- raise "Switching a machine's driver from #{machine_spec.reference['driver_url']} to #{driver_url} for is not currently supported! Use machine :destroy and then re-create the machine on the new driver."
549
+ def servers_for(specs_and_options)
550
+ result = {}
551
+ specs_and_options.each do |machine_spec, _machine_options|
552
+ if machine_spec.reference
553
+ if machine_spec.reference["driver_url"] != driver_url
554
+ raise "Switching a machine's driver from #{machine_spec.reference['driver_url']} to #{driver_url} for is not currently supported! Use machine :destroy and then re-create the machine on the new driver."
555
+ end
556
+ result[machine_spec] = compute.servers.get(machine_spec.reference["server_id"])
557
+ else
558
+ result[machine_spec] = nil
559
+ end
562
560
  end
563
- result[machine_spec] = compute.servers.get(machine_spec.reference['server_id'])
564
- else
565
- result[machine_spec] = nil
561
+ result
566
562
  end
567
- end
568
- result
569
- end
570
563
 
571
- @@chef_default_lock = Mutex.new
564
+ @@chef_default_lock = Mutex.new
572
565
 
573
- def overwrite_default_key_willy_nilly(action_handler, machine_spec)
574
- if machine_spec.reference &&
575
- Gem::Version.new(machine_spec.reference['driver_version']) < Gem::Version.new('0.10')
576
- return 'metal_default'
577
- end
566
+ def overwrite_default_key_willy_nilly(action_handler, machine_spec)
567
+ if machine_spec.reference &&
568
+ Gem::Version.new(machine_spec.reference["driver_version"]) < Gem::Version.new("0.10")
569
+ return "metal_default"
570
+ end
578
571
 
579
- driver = self
580
- updated = @@chef_default_lock.synchronize do
581
- Provisioning.inline_resource(action_handler) do
582
- fog_key_pair 'chef_default' do
583
- driver driver
584
- allow_overwrite true
572
+ driver = self
573
+ updated = @@chef_default_lock.synchronize do
574
+ Provisioning.inline_resource(action_handler) do
575
+ fog_key_pair "chef_default" do
576
+ driver driver
577
+ allow_overwrite true
578
+ end
579
+ end
580
+ end
581
+ if updated
582
+ # Only warn the first time
583
+ Chef::Log.warn("Using chef_default key, which is not shared between machines! It is recommended to create an AWS key pair with the fog_key_pair resource, and set :bootstrap_options => { :key_name => <key name> }")
585
584
  end
585
+ "chef_default"
586
586
  end
587
- end
588
- if updated
589
- # Only warn the first time
590
- Chef::Log.warn("Using chef_default key, which is not shared between machines! It is recommended to create an AWS key pair with the fog_key_pair resource, and set :bootstrap_options => { :key_name => <key name> }")
591
- end
592
- 'chef_default'
593
- end
594
587
 
595
- def bootstrap_options_for(machine_spec, machine_options)
596
- bootstrap_options = symbolize_keys(machine_options[:bootstrap_options] || {})
588
+ def bootstrap_options_for(machine_spec, machine_options)
589
+ bootstrap_options = symbolize_keys(machine_options[:bootstrap_options] || {})
597
590
 
598
- bootstrap_options[:tags] = default_tags(machine_spec, bootstrap_options[:tags] || {})
591
+ bootstrap_options[:tags] = default_tags(machine_spec, bootstrap_options[:tags] || {})
599
592
 
600
- bootstrap_options[:name] ||= machine_spec.name
593
+ bootstrap_options[:name] ||= machine_spec.name
601
594
 
602
- bootstrap_options
603
- end
595
+ bootstrap_options
596
+ end
604
597
 
605
- def default_tags(machine_spec, bootstrap_tags = {})
606
- tags = {
607
- 'Name' => machine_spec.name,
608
- 'BootstrapId' => machine_spec.id,
609
- 'BootstrapHost' => Socket.gethostname,
610
- 'BootstrapUser' => Etc.getlogin
611
- }
612
- # User-defined tags override the ones we set
613
- tags.merge(bootstrap_tags)
614
- end
598
+ def default_tags(machine_spec, bootstrap_tags = {})
599
+ tags = {
600
+ "Name" => machine_spec.name,
601
+ "BootstrapId" => machine_spec.id,
602
+ "BootstrapHost" => Socket.gethostname,
603
+ "BootstrapUser" => Etc.getlogin
604
+ }
605
+ # User-defined tags override the ones we set
606
+ tags.merge(bootstrap_tags)
607
+ end
615
608
 
616
- def machine_for(machine_spec, machine_options, server = nil)
617
- server ||= server_for(machine_spec)
618
- if !server
619
- raise "Server for node #{machine_spec.name} has not been created!"
620
- end
609
+ def machine_for(machine_spec, machine_options, server = nil)
610
+ server ||= server_for(machine_spec)
611
+ unless server
612
+ raise "Server for node #{machine_spec.name} has not been created!"
613
+ end
621
614
 
622
- if machine_spec.reference['is_windows']
623
- Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
624
- else
625
- Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
626
- end
627
- end
615
+ if machine_spec.reference["is_windows"]
616
+ Machine::WindowsMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
617
+ else
618
+ Machine::UnixMachine.new(machine_spec, transport_for(machine_spec, machine_options, server), convergence_strategy_for(machine_spec, machine_options))
619
+ end
620
+ end
628
621
 
629
- def volume_for(volume_spec)
630
- raise "Immplement me in #{self.class.name}"
631
- end
622
+ def volume_for(_volume_spec)
623
+ raise "Immplement me in #{self.class.name}"
624
+ end
632
625
 
633
- def convergence_strategy_for(machine_spec, machine_options)
634
- # Defaults
635
- if !machine_spec.reference
636
- return ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
637
- end
626
+ def convergence_strategy_for(machine_spec, machine_options)
627
+ # Defaults
628
+ unless machine_spec.reference
629
+ return ConvergenceStrategy::NoConverge.new(machine_options[:convergence_options], config)
630
+ end
638
631
 
639
- if machine_spec.reference['is_windows']
640
- ConvergenceStrategy::InstallMsi.new(machine_options[:convergence_options], config)
641
- elsif machine_options[:cached_installer] == true
642
- ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options], config)
643
- else
644
- ConvergenceStrategy::InstallSh.new(machine_options[:convergence_options], config)
645
- end
646
- end
632
+ if machine_spec.reference["is_windows"]
633
+ ConvergenceStrategy::InstallMsi.new(machine_options[:convergence_options], config)
634
+ elsif machine_options[:cached_installer] == true
635
+ ConvergenceStrategy::InstallCached.new(machine_options[:convergence_options], config)
636
+ else
637
+ ConvergenceStrategy::InstallSh.new(machine_options[:convergence_options], config)
638
+ end
639
+ end
647
640
 
648
- # Get the private key for a machine - prioritize the server data, fall back to the
649
- # the machine spec data, and if that doesn't work, raise an exception.
650
- # @param [Hash] machine_spec Machine spec data
651
- # @param [Hash] machine_options Machine options
652
- # @param [Chef::Provisioning::Machine] server a Machine representing the server
653
- # @return [String] PEM-encoded private key
654
- def private_key_for(machine_spec, machine_options, server)
655
- bootstrap_options = machine_options[:bootstrap_options] || {}
656
- if server.respond_to?(:private_key) && server.private_key
657
- server.private_key
658
- elsif server.respond_to?(:key_name) && server.key_name
659
- key = get_private_key(server.key_name)
660
- if !key
661
- raise "Server has key name '#{server.key_name}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
662
- end
663
- key
664
- elsif machine_spec.reference['key_name']
665
- key = get_private_key(machine_spec.reference['key_name'])
666
- if !key
667
- raise "Server was created with key name '#{machine_spec.reference['key_name']}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
668
- end
669
- key
670
- elsif bootstrap_options[:key_path]
671
- IO.read(bootstrap_options[:key_path])
672
- elsif bootstrap_options[:key_name]
673
- get_private_key(bootstrap_options[:key_name])
674
- elsif machine_options.has_key?(:ssh_options) && machine_options[:ssh_options].has_key?(:keys)
675
- IO.read(machine_options[:ssh_options][:keys].first)
676
- else
677
- # TODO make a way to suggest other keys to try ...
678
- raise "No key found to connect to #{machine_spec.name} (#{machine_spec.reference.inspect})" \
679
- " : machine_options -> (#{machine_options.inspect})!"
680
- end
681
- end
641
+ # Get the private key for a machine - prioritize the server data, fall back to the
642
+ # the machine spec data, and if that doesn't work, raise an exception.
643
+ # @param [Hash] machine_spec Machine spec data
644
+ # @param [Hash] machine_options Machine options
645
+ # @param [Chef::Provisioning::Machine] server a Machine representing the server
646
+ # @return [String] PEM-encoded private key
647
+ def private_key_for(machine_spec, machine_options, server)
648
+ bootstrap_options = machine_options[:bootstrap_options] || {}
649
+ if server.respond_to?(:private_key) && server.private_key
650
+ server.private_key
651
+ elsif server.respond_to?(:key_name) && server.key_name
652
+ key = get_private_key(server.key_name)
653
+ unless key
654
+ raise "Server has key name '#{server.key_name}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
655
+ end
656
+ key
657
+ elsif machine_spec.reference["key_name"]
658
+ key = get_private_key(machine_spec.reference["key_name"])
659
+ unless key
660
+ raise "Server was created with key name '#{machine_spec.reference['key_name']}', but the corresponding private key was not found locally. Check if the key is in Chef::Config.private_key_paths: #{Chef::Config.private_key_paths.join(', ')}"
661
+ end
662
+ key
663
+ elsif bootstrap_options[:key_path]
664
+ IO.read(bootstrap_options[:key_path])
665
+ elsif bootstrap_options[:key_name]
666
+ get_private_key(bootstrap_options[:key_name])
667
+ elsif machine_options.key?(:ssh_options) && machine_options[:ssh_options].key?(:keys)
668
+ IO.read(machine_options[:ssh_options][:keys].first)
669
+ else
670
+ # TODO: make a way to suggest other keys to try ...
671
+ raise "No key found to connect to #{machine_spec.name} (#{machine_spec.reference.inspect})" \
672
+ " : machine_options -> (#{machine_options.inspect})!"
673
+ end
674
+ end
682
675
 
683
- def ssh_options_for(machine_spec, machine_options, server)
684
- result = {
685
- :auth_methods => [ 'publickey' ],
686
- :host_key_alias => "#{server.id}.#{provider}"
687
- }.merge(machine_options[:ssh_options] || {})
688
- # Grab key_data from the user's config if not specified
689
- unless result.has_key?(:key_data)
690
- result[:keys_only] = true
691
- result[:key_data] = [ private_key_for(machine_spec, machine_options, server) ]
692
- end
693
- result
694
- end
676
+ def ssh_options_for(machine_spec, machine_options, server)
677
+ result = {
678
+ auth_methods: ["publickey"],
679
+ host_key_alias: "#{server.id}.#{provider}"
680
+ }.merge(machine_options[:ssh_options] || {})
681
+ # Grab key_data from the user's config if not specified
682
+ unless result.key?(:key_data)
683
+ result[:keys_only] = true
684
+ result[:key_data] = [private_key_for(machine_spec, machine_options, server)]
685
+ end
686
+ result
687
+ end
695
688
 
696
- def default_ssh_username
697
- 'root'
698
- end
689
+ def default_ssh_username
690
+ "root"
691
+ end
699
692
 
700
- def create_winrm_transport(machine_spec, machine_options, server)
701
- fail "This provider doesn't know how to do that."
702
- end
693
+ def create_winrm_transport(_machine_spec, _machine_options, _server)
694
+ raise "This provider doesn't know how to do that."
695
+ end
703
696
 
704
- def create_ssh_transport(machine_spec, machine_options, server)
705
- ssh_options = ssh_options_for(machine_spec, machine_options, server)
706
- username = machine_spec.reference['ssh_username'] || default_ssh_username
707
- if machine_options.has_key?(:ssh_username) && machine_options[:ssh_username] != machine_spec.reference['ssh_username']
708
- 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.")
709
- end
710
- options = {}
711
- if machine_spec.reference[:sudo] || (!machine_spec.reference.has_key?(:sudo) && username != 'root')
712
- options[:prefix] = 'sudo '
713
- end
697
+ def create_ssh_transport(machine_spec, machine_options, server)
698
+ ssh_options = ssh_options_for(machine_spec, machine_options, server)
699
+ username = machine_spec.reference["ssh_username"] || default_ssh_username
700
+ if machine_options.key?(:ssh_username) && machine_options[:ssh_username] != machine_spec.reference["ssh_username"]
701
+ 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.")
702
+ end
703
+ options = {}
704
+ if machine_spec.reference[:sudo] || (!machine_spec.reference.key?(:sudo) && username != "root")
705
+ options[:prefix] = "sudo "
706
+ end
714
707
 
715
- remote_host = determine_remote_host(machine_spec, server)
716
- if remote_host.nil? || remote_host.empty?
717
- raise "Server #{server.id} has no private or public IP address!"
718
- end
708
+ remote_host = determine_remote_host(machine_spec, server)
709
+ if remote_host.nil? || remote_host.empty?
710
+ raise "Server #{server.id} has no private or public IP address!"
711
+ end
719
712
 
720
- #Enable pty by default
721
- options[:ssh_pty_enable] = true
722
- if machine_options[:ssh_gateway]
723
- options[:ssh_gateway] = machine_options[:ssh_gateway]
724
- elsif machine_spec.reference.has_key?('ssh_gateway')
725
- options[:ssh_gateway] = machine_spec.reference['ssh_gateway']
726
- end
713
+ # Enable pty by default
714
+ options[:ssh_pty_enable] = true
715
+ if machine_options[:ssh_gateway]
716
+ options[:ssh_gateway] = machine_options[:ssh_gateway]
717
+ elsif machine_spec.reference.key?("ssh_gateway")
718
+ options[:ssh_gateway] = machine_spec.reference["ssh_gateway"]
719
+ end
727
720
 
728
- Transport::SSH.new(remote_host, username, ssh_options, options, config)
729
- end
721
+ Transport::SSH.new(remote_host, username, ssh_options, options, config)
722
+ end
730
723
 
731
- def self.compute_options_for(provider, id, config)
732
- raise "unsupported Fog provider #{provider}"
733
- end
724
+ def self.compute_options_for(provider, _id, _config)
725
+ raise "unsupported Fog provider #{provider}"
726
+ end
734
727
 
735
- def determine_remote_host(machine_spec, server)
736
- transport_address_location = (machine_spec.reference['transport_address_location'] || :none).to_sym
737
-
738
- if machine_spec.reference['use_private_ip_for_ssh']
739
- # The machine_spec has the old config key, lets update it - a successful chef converge will save the machine_spec
740
- # TODO in 2.0 get rid of this update
741
- machine_spec.reference.delete('use_private_ip_for_ssh')
742
- machine_spec.reference['transport_address_location'] = :private_ip
743
- server.private_ip_address
744
- elsif transport_address_location == :ip_addresses
745
- server.ip_addresses.first
746
- elsif transport_address_location == :private_ip
747
- server.private_ip_address
748
- elsif transport_address_location == :public_ip
749
- server.public_ip_address
750
- elsif !server.public_ip_address && server.private_ip_address
751
- Chef::Log.warn("Server #{machine_spec.name} has no public floating_ip address. Using private floating_ip '#{server.private_ip_address}'. Set driver option 'transport_address_location' => :private_ip if this will always be the case ...")
752
- server.private_ip_address
753
- elsif server.public_ip_address
754
- server.public_ip_address
755
- else
756
- raise "Server #{server.id} has no private or public IP address!"
757
- # raise "Invalid 'transport_address_location'. They can only be 'public_ip', 'private_ip', or 'ip_addresses'."
728
+ def determine_remote_host(machine_spec, server)
729
+ transport_address_location = (machine_spec.reference["transport_address_location"] || :none).to_sym
730
+
731
+ if machine_spec.reference["use_private_ip_for_ssh"]
732
+ # The machine_spec has the old config key, lets update it - a successful chef converge will save the machine_spec
733
+ # TODO in 2.0 get rid of this update
734
+ machine_spec.reference.delete("use_private_ip_for_ssh")
735
+ machine_spec.reference["transport_address_location"] = :private_ip
736
+ server.private_ip_address
737
+ elsif transport_address_location == :ip_addresses
738
+ server.ip_addresses.first
739
+ elsif transport_address_location == :private_ip
740
+ server.private_ip_address
741
+ elsif transport_address_location == :public_ip
742
+ server.public_ip_address
743
+ elsif !server.public_ip_address && server.private_ip_address
744
+ Chef::Log.warn("Server #{machine_spec.name} has no public floating_ip address. Using private floating_ip '#{server.private_ip_address}'. Set driver option 'transport_address_location' => :private_ip if this will always be the case ...")
745
+ server.private_ip_address
746
+ elsif server.public_ip_address
747
+ server.public_ip_address
748
+ else
749
+ raise "Server #{server.id} has no private or public IP address!"
750
+ # raise "Invalid 'transport_address_location'. They can only be 'public_ip', 'private_ip', or 'ip_addresses'."
751
+ end
752
+ end
758
753
  end
759
754
  end
760
755
  end
761
756
  end
762
- end
763
- end