class Inception::InceptionServer

Constants

DEFAULT_DISK_SIZE
DEFAULT_FLAVOR
DEFAULT_SECURITY_GROUPS
DEFAULT_SERVER_NAME

Attributes

attributes[R]

Public Class Methods

new(provider_client, attributes, ssh_dir) click to toggle source

@provider_client [Inception::Providers::FogProvider] - interact with IaaS @attributes [ReadWriteSettings]

Required @attributes:

{
  "name" => "inception",
  "ip_address" => "54.214.15.178",
  "key_pair" => {
    "name" => "inception",
    "private_key" => "private_key",
    "public_key" => "public_key"
  }
}

Including optional @attributes and default values:

{
  "name" => "inception",
  "ip_address" => "54.214.15.178",
  "security_groups" => ["ssh"],
  "flavor" => "m1.small",
  "key_pair" => {
    "name" => "inception",
    "private_key" => "private_key",
    "public_key" => "public_key"
  }
}
# File lib/inception/inception_server.rb, line 39
def initialize(provider_client, attributes, ssh_dir)
  @provider_client = provider_client
  @ssh_dir = ssh_dir
  @attributes = attributes.is_a?(Hash) ? ReadWriteSettings.new(attributes) : attributes
  raise "@attributes must be ReadWriteSettings (or Hash)" unless @attributes.is_a?(ReadWriteSettings)
end

Public Instance Methods

create() click to toggle source

Create the underlying server, with key pair & security groups, unless it is already created

The @attributes hash is updated with a `provisioned` key during/after creation. When saved as YAML it might look like:

inception:
  provisioned:
    image_id: ami-123456
    server_id: i-e7f005d2
    security_groups:
      - ssh
      - mosh
    username: ubuntu
    disk_device: /dev/sdi
    host: ec2-54-214-15-178.us-west-2.compute.amazonaws.com
    validated: true
    converged: true
# File lib/inception/inception_server.rb, line 62
def create
  validate_attributes_for_bootstrap
  ensure_required_security_groups
  create_missing_default_security_groups
  bootstrap_vm
  attach_persistent_disk
end
default_disk_device() click to toggle source
# File lib/inception/inception_server.rb, line 192
def default_disk_device
  case @provider_client
  when Inception::Providers::Clients::AwsProviderClient
    { "external" => "/dev/sdf", "internal" => "/dev/xvdf" }
  when Inception::Providers::Clients::OpenStackProviderClient
    { "external" => "/dev/vdc", "internal" => "/dev/vdc" }
  else
    raise "Please implement InceptionServer#default_disk_device for #{@provider_client.class}"
  end
end
delete_all() click to toggle source

Delete the server, volume and release the IP address

# File lib/inception/inception_server.rb, line 71
def delete_all
  delete_server
  delete_volume
  delete_key_pair
  release_ip_address
end
delete_key_pair() click to toggle source
# File lib/inception/inception_server.rb, line 106
def delete_key_pair
  key_pair_name = attributes.exists?("key_pair.name")
  if key_pair_name && key_pair = fog_compute.key_pairs.get(key_pair_name)
    puts "Deleting key pair '#{key_pair_name}'"
    key_pair.destroy
  else
    puts "Keypair already destroyed"
  end
  attributes.delete("key_pair")
end
delete_server() click to toggle source
# File lib/inception/inception_server.rb, line 78
def delete_server
  @fog_server = nil # force reload of fog_server model
  if fog_server
    print "Deleting server... "
    fog_server.destroy
    wait_for_termination(fog_server) unless Fog.mocking?
    puts "done."
  else
    puts "Server already destroyed"
  end
  provisioned.delete("host")
  provisioned.delete("server_id")
  provisioned.delete("username")
end
delete_volume() click to toggle source
# File lib/inception/inception_server.rb, line 93
def delete_volume
  volume_id = provisioned.exists?("disk_device.volume_id")
  if volume_id && (volume = fog_compute.volumes.get(volume_id)) && volume.ready?
    print "Deleting volume... "
    volume.destroy
    wait_for_termination(volume, "deleting")
    puts ""
  else
    puts "Volume already destroyed"
  end
  provisioned.delete("disk_device")
end
disk_devices() click to toggle source
# File lib/inception/inception_server.rb, line 184
def disk_devices
  provisioned["disk_device"] ||= default_disk_device
end
disk_size() click to toggle source

Size of attached persistent disk for the inception server

# File lib/inception/inception_server.rb, line 157
def disk_size
  @attributes["disk_size"] ||= DEFAULT_DISK_SIZE
end
export_attributes() click to toggle source

Because @attributes is not the same as @attributes.provisioned we need a helper to export the complete nested attributes.

# File lib/inception/inception_server.rb, line 178
def export_attributes
  attrs = attributes.to_nested_hash
  attrs["provisioned"] = provisioned.to_nested_hash
  attrs
end
external_disk_device() click to toggle source
# File lib/inception/inception_server.rb, line 188
def external_disk_device
  disk_devices["external"]
end
flavor() click to toggle source

Flavor/instance type of the server to be provisioned TODO: DEFAULT_FLAVOR should become IaaS/provider specific

# File lib/inception/inception_server.rb, line 152
def flavor
  @attributes["flavor"] ||= DEFAULT_FLAVOR
end
fog_compute() click to toggle source
# File lib/inception/inception_server.rb, line 215
def fog_compute
  @provider_client.fog_compute
end
fog_server() click to toggle source
# File lib/inception/inception_server.rb, line 207
def fog_server
  @fog_server ||= begin
    if server_id = provisioned["server_id"]
      fog_compute.servers.get(server_id)
    end
  end
end
image_id() click to toggle source
# File lib/inception/inception_server.rb, line 165
def image_id
  @attributes["image_id"] ||= @provider_client.image_id
end
ip_address() click to toggle source
# File lib/inception/inception_server.rb, line 161
def ip_address
  provisioned.ip_address
end
key_name() click to toggle source
# File lib/inception/inception_server.rb, line 138
def key_name
  @attributes.key_pair.name
end
private_key_path() click to toggle source
# File lib/inception/inception_server.rb, line 142
def private_key_path
  @private_key_path ||= File.join(@ssh_dir, key_name)
end
provisioned() click to toggle source

The progresive/final attributes of the provisioned Inception server & persistent disk.

# File lib/inception/inception_server.rb, line 171
def provisioned
  @attributes["provisioned"] = {} unless @attributes["provisioned"]
  @attributes.provisioned
end
public_key() click to toggle source
# File lib/inception/inception_server.rb, line 146
def public_key
  @attributes.exists?("key_pair.public_key")
end
release_ip_address() click to toggle source
# File lib/inception/inception_server.rb, line 118
def release_ip_address
  public_ip = provisioned.exists?("ip_address")
  if public_ip && ip_address = fog_compute.addresses.get(public_ip)
    puts "Releasing IP address #{public_ip}"
    ip_address.destroy
  else
    puts "IP address already released"
  end
  provisioned.delete("ip_address")
end
security_groups() click to toggle source
# File lib/inception/inception_server.rb, line 129
def security_groups
  @attributes.security_groups
end
server_name() click to toggle source
# File lib/inception/inception_server.rb, line 133
def server_name
  @attributes["name"] ||= DEFAULT_SERVER_NAME
  @attributes.name
end
user_host() click to toggle source
# File lib/inception/inception_server.rb, line 203
def user_host
  "#{provisioned.username}@#{provisioned.host}"
end

Protected Instance Methods

attach_persistent_disk() click to toggle source
# File lib/inception/inception_server.rb, line 268
def attach_persistent_disk
  unless Fog.mocking?
    print "Confirming ssh access to server... "
    Fog.wait_for(60) { fog_server.sshable?(ssh_options) }
    puts "done"
  end

  unless volume = @provider_client.find_server_device(fog_server, external_disk_device)
    print "Provisioning #{disk_size}Gb persistent disk for inception server... "
    volume = @provider_client.create_and_attach_volume("Inception Disk", disk_size, fog_server, external_disk_device)
    disk_devices["volume_id"] = volume.id
    puts disk_devices.volume_id
  end
  set_resource_name(volume, server_name)
end
bootstrap_vm() click to toggle source
# File lib/inception/inception_server.rb, line 256
def bootstrap_vm
  unless fog_server
    print "Booting #{flavor} inception server... "
    @fog_server = @provider_client.bootstrap(fog_attributes)
    provisioned["server_id"] = fog_server.id
    provisioned["host"] = fog_server.dns_name || fog_server.public_ip_address
    provisioned["username"] = fog_attributes[:username]
    puts provisioned.server_id
  end
  set_resource_name(fog_server, server_name)
end
create_missing_default_security_groups() click to toggle source
# File lib/inception/inception_server.rb, line 251
def create_missing_default_security_groups
  # provider method only creates group if missing
  @provider_client.create_security_group("ssh", "ssh", {ssh: 22})
end
ensure_required_security_groups() click to toggle source

ssh group must be first (bootstrap method looks for port 22 in first group)

# File lib/inception/inception_server.rb, line 241
def ensure_required_security_groups
  if @attributes["security_groups"] && @attributes["security_groups"].is_a?(Array)
    unless @attributes["security_groups"].include?("ssh")
      @attributes["security_groups"] = ["ssh", *@attributes["security_groups"]]
    end
  else
    @attributes["security_groups"] = ["ssh"]
  end
end
fog_attributes() click to toggle source
# File lib/inception/inception_server.rb, line 227
def fog_attributes
  @provider_client.fog_attributes(self)
end
say(*args) click to toggle source

TODO emit events rather than writing directly to STDOUT

# File lib/inception/inception_server.rb, line 300
def say(*args)
  puts(*args)
end
set_resource_name(resource, name) click to toggle source

set_resource_name(fog_server, “inception”) set_resource_name(volume, “inception-root”) set_resource_name(volume, “inception-store”)

# File lib/inception/inception_server.rb, line 223
def set_resource_name(resource, name)
  @provider_client.set_resource_name(resource, name)
end
ssh_options() click to toggle source
# File lib/inception/inception_server.rb, line 284
def ssh_options
  {
    keys: [private_key_path]
  }
end
validate_attributes_for_bootstrap() click to toggle source
# File lib/inception/inception_server.rb, line 231
def validate_attributes_for_bootstrap
  missing_attributes = []
  missing_attributes << "provisioned.ip_address" unless @attributes.exists?("provisioned.ip_address")
  missing_attributes << "key_pair.private_key" unless @attributes.exists?("key_pair.private_key")
  if missing_attributes.size > 0
    raise "Missing InceptionServer attributes: #{missing_attributes.join(', ')}"
  end
end
wait_for_termination(fog_model, state_to_wait_for="terminated") click to toggle source

Poll a fog model until it terminates; print . each second

# File lib/inception/inception_server.rb, line 291
def wait_for_termination(fog_model, state_to_wait_for="terminated")
  fog_model.wait_for do
    print "."
    state == state_to_wait_for
  end
end