class Cult::Drivers::VirtualBoxDriver

Public Class Methods

new(api_key:) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 7
def initialize(api_key:)
end
setup!() click to toggle source
Calls superclass method Cult::Driver::setup!
# File lib/cult/drivers/virtual_box_driver.rb, line 154
def self.setup!
  super

  inst = new(api_key: nil)

  return {
    driver: driver_name,
    api_key: nil,
    configurations: {
      sizes:  inst.sizes,
      zones:  inst.zones,
      images: inst.images,
    }
  }
end

Public Instance Methods

await_ip_address(name, index, protocol) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 69
def await_ip_address(name, index, protocol)
  puts "Awaiting IP address from VirtualBox Guest Additions"
  unset_ip_data(name, index, protocol)

  backoff_loop do
    if (ip = get_ip_data(name, index, protocol))
      return ip
    end
  end
end
destroy!(id:, ssh_key_id:) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 81
def destroy!(id:, ssh_key_id:)
  system 'VBoxManage', 'controlvm', id, 'poweroff'
  system 'VBoxManage', 'unregistervm', id, '--delete'
end
esc(s) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 44
def esc(s)
  Shellwords.escape(s)
end
get_ip_data(name, index, protocol) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 56
def get_ip_data(name, index, protocol)
  cmd = "VBoxManage guestproperty get #{esc(name)} " +
        "#{ip_property_name(index, protocol)}"
  s = `#{cmd}`

  if $?.success? && (m = s.match(/^Value: (.+)$/))
    m[1]
  else
    nil
  end
end
guest_command(name, cmd) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 98
def guest_command(name, cmd)
  cmd = "VBoxManage guestcontrol #{esc(name)} " +
        "--username root --password password " +
        "run -- /bin/sh -c #{esc(cmd)}"
  puts cmd
  `#{cmd}`
end
guest_copy(name, src, dst) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 87
def guest_copy(name, src, dst)
  # NOTE: Bug in current (Sep 2016) VBox has a fucked copyto, where
  # setting target-directory to the full path is a workaround
  cmd = "VBoxManage guestcontrol #{esc(name)} " +
        "--username root --password password " +
        "copyto #{esc(src)} --target-directory #{esc(dst)}"
  puts cmd
  `#{cmd}`
end
images_map() click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 21
def images_map
  %x(VBoxManage list vms).each_line.map do |line|
    words = Shellwords.split(line.chomp)
    [ distro_name(words[0]), words[1] ]
  end.to_h
end
ip_property_name(index, protocol) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 35
def ip_property_name(index, protocol)
  protocols = {
    ipv4: 'V4',
    ipv6: 'V6'
  }
  "/VirtualBox/GuestInfo/Net/#{index}/#{protocols[protocol]}/IP"
end
provision!(name:, size:, zone:, image:, ssh_public_key:) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 107
def provision!(name:, size:, zone:, image:, ssh_public_key:)
  transaction do |xac|
    # Todo: transaction
    system 'VBoxManage', 'clonevm',
            fetch_mapped(name: :image, from: images_map, key: image),
           '--name', name, '--register'

    xac.rollback do
      destroy!(id: name, ssh_key_id: nil)
    end

    system_spec = sizes_map[size]
    system 'VBoxManage', 'modifyvm', name,
                         '--groups', '/Cult',
                         '--memory', system_spec[:ram].to_s,
                         '--cpus', system_spec[:cores].to_s

    system 'VBoxManage', 'startvm', name, '--type', 'headless'

    public_ip  = await_ip_address(name, 0, :ipv4)
    private_ip = public_ip

    await_ssh(public_ip)

    guest_command(name, "mkdir -m 0600 /root/.ssh")
    guest_copy(name, ssh_public_key, "/root/.ssh/authorized_keys")
    guest_command(name, "chmod 0644 /root/.ssh/authorized_keys")
    guest_command(name, "passwd -l root")

    return {
        name:          name,
        size:          size,
        zone:          zone,
        image:         image,

        id:           name,
        created_at:   Time.now.iso8601,
        host:         public_ip,
        ipv4_public:  public_ip,
        ipv4_private: private_ip,
        ipv6_public:  nil,
        ipv6_private: nil,
        meta:         {}
    }
  end
end
sizes_map() click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 11
def sizes_map
  # Cores = GB Ram is a pretty good scaling factor, and
  # closely maps what VPS providers are doing.
  [1, 2, 4, 6, 8, 10, 12, 16].map do |size|
    [ "#{size}gb", { ram: size * 1024, cores: size } ]
  end.to_h
end
unset_ip_data(name, index, protocol) click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 49
def unset_ip_data(name, index, protocol)
  cmd = "VBoxManage guestproperty unset #{esc(name)} " +
        "#{ip_property_name(index, protocol)}"
  `#{cmd}`
end
zones() click to toggle source
# File lib/cult/drivers/virtual_box_driver.rb, line 30
def zones
  ['local']
end