chef-provisioning-fog 0.26.1 → 0.26.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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