Class: ChefMetalFog::FogDriver
- Inherits:
-
ChefMetal::Driver
- Object
- ChefMetal::Driver
- ChefMetalFog::FogDriver
- Includes:
- Chef::Mixin::ShellOut
- Defined in:
- lib/chef_metal_fog/fog_driver.rb
Overview
Provisions cloud machines with the Fog driver.
## Fog Driver URLs
All Metal drivers use URLs to uniquely identify a driver’s “bucket” of machines. Fog URLs are of the form fog:<provider>:<identifier:> - see individual providers for sample URLs.
Identifier is generally something uniquely identifying the account. If multiple users can access the account, the identifier should be the same for all of them (do not use the username in these cases, use an account ID or auth server URL).
In particular, the identifier should be specific enough that if you create a server with a driver with this URL, the server should be retrievable from the same URL *no matter what else changes*. For example, an AWS account ID is not enough for this–if you varied the region, you would no longer see your server in the list. Thus, AWS uses both the account ID and the region.
## Supporting a new Fog provider
The Fog driver does not immediately support all Fog providers out of the box. Some minor work needs to be done to plug them into metal.
To add a new supported Fog provider, pick an appropriate identifier, go to from_provider and compute_options_for, and add the new provider in the case statements so that URLs for your fog provider can be generated. If your cloud provider has environment variables or standard config files (like ~/.aws/config), you can read those and merge that information in the compute_options_for function.
## Location format
All machines have a location hash to find them. These are the keys used by the fog provisioner:
-
driver_url: fog:<driver>:<unique_account_info>
-
server_id: the ID of the server so it can be found again
-
created_at: timestamp server was created
-
started_at: timestamp server was last started
-
is_windows, ssh_username, sudo, use_private_ip_for_ssh: copied from machine_options
## Machine options
Machine options (for allocation and readying the machine) include:
-
bootstrap_options: hash of options to pass to compute.servers.create
-
is_windows: true if windows. TODO detect this from ami?
-
create_timeout: the time to wait for the instance to boot to ssh (defaults to 600)
-
start_timeout: the time to wait for the instance to start (defaults to 600)
-
ssh_timeout: the time to wait for ssh to be available if the instance is detected as up (defaults to 20)
-
ssh_username: username to use for ssh
-
sudo: true to prefix all commands with “sudo”
-
use_private_ip_for_ssh: hint to use private ip when available
-
convergence_options: hash of options for the convergence strategy
-
chef_client_timeout: the time to wait for chef-client to finish
-
chef_server - the chef server to point convergence at
-
Example bootstrap_options for ec2:
:bootstrap_options => {
:image_id =>'ami-311f2b45',
:flavor_id =>'t1.micro',
:key_name => 'key-pair-name'
}
Direct Known Subclasses
Providers::AWS, Providers::CloudStack, Providers::DigitalOcean, Providers::OpenStack, Providers::Rackspace
Constant Summary collapse
- DEFAULT_OPTIONS =
{ :create_timeout => 180, :start_timeout => 180, :ssh_timeout => 20 }
- @@registered_provider_classes =
{}
Class Method Summary collapse
- .__new__ ⇒ Object
- .canonicalize_url(driver_url, config) ⇒ Object
-
.from_provider(provider, config) ⇒ Object
Passed in a config which is not merged with driver_url (because we don’t know what it is yet) but which has the same keys.
-
.from_url(driver_url, config) ⇒ Object
Passed in a driver_url, and a config in the format of Driver.config.
- .inherited(klass) ⇒ Object
- .new(driver_url, config) ⇒ Object
- .provider_class_for(provider) ⇒ Object
- .register_provider_class(name, driver) ⇒ Object
Instance Method Summary collapse
-
#allocate_machine(action_handler, machine_spec, machine_options) ⇒ Object
Acquire a machine, generally by provisioning it.
- #compute ⇒ Object
- #compute_options ⇒ Object
-
#connect_to_machine(machine_spec, machine_options) ⇒ Object
Connect to machine without acquiring it.
- #destroy_machine(action_handler, machine_spec, machine_options) ⇒ Object
-
#initialize(driver_url, config) ⇒ FogDriver
constructor
Create a new fog driver.
- #provider ⇒ Object
- #ready_machine(action_handler, machine_spec, machine_options) ⇒ Object
- #stop_machine(action_handler, machine_spec, machine_options) ⇒ Object
-
#transport_for(machine_spec, machine_options, server) ⇒ Object
Not meant to be part of public interface.
Constructor Details
#initialize(driver_url, config) ⇒ FogDriver
Create a new fog driver.
## Parameters driver_url - URL of driver. “fog:<provider>:<provider_id>” config - configuration. :driver_options, :keys, :key_paths and :log_level are used.
driver_options is a hash with these possible options:
- compute_options: the hash of options to Fog::Compute.new.
- aws_config_file: aws config file (default: ~/.aws/config)
- aws_csv_file: aws csv credentials file downloaded from EC2 interface
- aws_profile: profile name to use for credentials
- aws_credentials: AWSCredentials object. (will be created for you by default)
- log_level: :debug, :info, :warn, :error
156 157 158 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 156 def initialize(driver_url, config) super(driver_url, config) end |
Class Method Details
.__new__ ⇒ Object
98 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 98 alias :__new__ :new |
.canonicalize_url(driver_url, config) ⇒ Object
127 128 129 130 131 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 127 def self.canonicalize_url(driver_url, config) _, provider, id = driver_url.split(':', 3) config, id = provider_class_for(provider).(provider, id, config) [ "fog:#{provider}:#{id}", config ] end |
.from_provider(provider, config) ⇒ Object
Passed in a config which is not merged with driver_url (because we don’t know what it is yet) but which has the same keys
135 136 137 138 139 140 141 142 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 135 def self.from_provider(provider, config) # Figure out the options and merge them into the config config, id = provider_class_for(provider).(provider, nil, config) driver_url = "fog:#{provider}:#{id}" ChefMetal.driver_for_url(driver_url, config) end |
.from_url(driver_url, config) ⇒ Object
Passed in a driver_url, and a config in the format of Driver.config.
123 124 125 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 123 def self.from_url(driver_url, config) FogDriver.new(driver_url, config) end |
.inherited(klass) ⇒ Object
100 101 102 103 104 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 100 def inherited(klass) class << klass alias :new :__new__ end end |
.new(driver_url, config) ⇒ Object
117 118 119 120 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 117 def self.new(driver_url, config) provider = driver_url.split(':')[1] provider_class_for(provider).new(driver_url, config) end |
.provider_class_for(provider) ⇒ Object
112 113 114 115 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 112 def self.provider_class_for(provider) require "chef_metal_fog/providers/#{provider.downcase}" @@registered_provider_classes[provider] end |
.register_provider_class(name, driver) ⇒ Object
108 109 110 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 108 def self.register_provider_class(name, driver) @@registered_provider_classes[name] = driver end |
Instance Method Details
#allocate_machine(action_handler, machine_spec, machine_options) ⇒ Object
Acquire a machine, generally by provisioning it. Returns a Machine object pointing at the machine, allowing useful actions like setup, converge, execute, file and directory.
171 172 173 174 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 171 def allocate_machine(action_handler, machine_spec, ) # If the server does not exist, create it create_server(action_handler, machine_spec, ) end |
#compute ⇒ Object
234 235 236 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 234 def compute @compute ||= Fog::Compute.new() end |
#compute_options ⇒ Object
160 161 162 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 160 def [:compute_options].to_hash || {} end |
#connect_to_machine(machine_spec, machine_options) ⇒ Object
Connect to machine without acquiring it
209 210 211 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 209 def connect_to_machine(machine_spec, ) machine_for(machine_spec, ) end |
#destroy_machine(action_handler, machine_spec, machine_options) ⇒ Object
213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 213 def destroy_machine(action_handler, machine_spec, ) server = server_for(machine_spec) if server action_handler.perform_action "destroy machine #{machine_spec.name} (#{machine_spec.location['server_id']} at #{driver_url})" do server.destroy machine_spec.location = nil end end strategy = convergence_strategy_for(machine_spec, ) strategy.cleanup_convergence(action_handler, machine_spec) end |
#provider ⇒ Object
164 165 166 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 164 def provider [:provider] end |
#ready_machine(action_handler, machine_spec, machine_options) ⇒ Object
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 176 def ready_machine(action_handler, machine_spec, ) server = server_for(machine_spec) if server.nil? raise "Machine #{machine_spec.name} does not have a server associated with it, or server does not exist." end # Attach floating IPs if necessary attach_floating_ips(action_handler, machine_spec, , server) # Start the server if needed, and wait for it to start start_server(action_handler, machine_spec, server) wait_until_ready(action_handler, machine_spec, , server) begin wait_for_transport(action_handler, machine_spec, , server) rescue Fog::Errors::TimeoutError # Only ever reboot once, and only if it's been less than 10 minutes since we stopped waiting if machine_spec.location['started_at'] || remaining_wait_time(machine_spec, ) < -(10*60) raise else # Sometimes (on EC2) the machine comes up but gets stuck or has # some other problem. If this is the case, we restart the server # to unstick it. Reboot covers a multitude of sins. 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 ..." restart_server(action_handler, machine_spec, server) wait_until_ready(action_handler, machine_spec, , server) wait_for_transport(action_handler, machine_spec, , server) end end machine_for(machine_spec, , server) end |
#stop_machine(action_handler, machine_spec, machine_options) ⇒ Object
225 226 227 228 229 230 231 232 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 225 def stop_machine(action_handler, machine_spec, ) server = server_for(machine_spec) if server action_handler.perform_action "stop machine #{machine_spec.name} (#{server.id} at #{driver_url})" do server.stop end end end |
#transport_for(machine_spec, machine_options, server) ⇒ Object
Not meant to be part of public interface
239 240 241 242 |
# File 'lib/chef_metal_fog/fog_driver.rb', line 239 def transport_for(machine_spec, , server) # TODO winrm create_ssh_transport(machine_spec, , server) end |