class Cult::Drivers::VultrDriver

Attributes

api_key[R]

Public Class Methods

new(api_key:) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 11
def initialize(api_key:)
  @api_key = api_key
end
setup!() click to toggle source
Calls superclass method Cult::Driver::setup!
# File lib/cult/drivers/vultr_driver.rb, line 175
def self.setup!
  super
  url = "https://my.vultr.com/settings/#settingsapi"
  puts "Vultr does not generate multiple API keys, so you'll need to "
       "create one (if it does not exist).  You can access your API key "
       "at the following URL:"
  puts
  puts "  #{url}"
  puts

  CLI.launch_browser(url) if CLI.yes_no?("Launch browser?")

  api_key = CLI.prompt("API Key")

  unless api_key.match(/^[A-Z2-7]{36}$/)
    puts "That doesn't look like an API key, but I'll trust you"
  end

  inst = new(api_key: api_key)
  return {
    api_key: api_key,
    driver: driver_name,
    configurations: {
      sizes:  inst.sizes,
      zones:  inst.zones,
      images: inst.images,
    }
  }
end
with_api_key(method_name) click to toggle source

This sets the Vultr API key to this instance's api key for the duration of a method call and restores it afterwards.

# File lib/cult/drivers/vultr_driver.rb, line 18
def self.with_api_key(method_name)
  unwrapped_name = "#{method_name}_no_api_key".to_sym
  alias_method unwrapped_name, method_name
  define_method(method_name) do |*args, &block|
    old_api_key = Vultr.api_key
    begin
      Vultr.api_key = self.api_key
      return send(unwrapped_name, *args, &block)
    ensure
      Vultr.api_key = old_api_key
    end
  end
end

Public Instance Methods

destroy!(id:, ssh_key_id: nil) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 104
def destroy!(id:, ssh_key_id: nil)
  Vultr::Server.destroy(SUBID: id)
  destroy_ssh_key!(ssh_key_id: ssh_key_id) if ssh_key_id
end
destroy_ssh_key!(ssh_key_id:) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 110
def destroy_ssh_key!(ssh_key_id:)
  Vultr::SSHKey.destroy(SSHKEYID: ssh_key_id)
end
fetch_ip(list, type) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 97
def fetch_ip(list, type)
  goal = (type == :public ? "main_ip" : "private")
  r = list.find{ |v| v["type"] == goal }
  r.nil? ? nil : r["ip"]
end
images_map() click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 43
def images_map
  Vultr::OS.list[:result].select do |k, v|
    # Doing our part to kill x86/32
    v['arch'] == 'x64'
  end.map do |k,v|
    [slugify(distro_name(v["name"])), v["OSID"]]
  end.reject do |k,v|
    %w(custom snapshot backup application).include?(k) ||
    k.match(/^windows/)
  end.to_h
end
provision!(name:, size:, zone:, image:, ssh_public_key:) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 116
def provision!(name:, size:, zone:, image:, ssh_public_key:)
  transaction do |xac|
    ssh_key_id = upload_ssh_key(file: ssh_public_key)
    xac.rollback do
      destroy_ssh_key!(ssh_key_id: ssh_key_id)
    end

    sizeid  = fetch_mapped(name: :size, from: sizes_map, key: size)
    imageid = fetch_mapped(name: :image, from: images_map, key: image)
    zoneid  = fetch_mapped(name: :zone, from: zones_map, key: zone)

    r = Vultr::Server.create(DCID: zoneid,
                             OSID: imageid,
                             VPSPLANID: sizeid,
                             enable_ipv6: 'yes',
                             enable_private_network: 'yes',
                             label: name,
                             hostname: name,
                             SSHKEYID: ssh_key_id)

    subid = r[:result]["SUBID"]
    xac.rollback do
      destroy!(id: subid)
    end

    # Wait until it's active, it won't have an IP until then
    backoff_loop do
      r = Vultr::Server.list(SUBID: subid)[:result]
      break if r['status'] == 'active'
    end

    iplist4 = Vultr::Server.list_ipv4(SUBID: subid)[:result].values[0]
    iplist6 = Vultr::Server.list_ipv6(SUBID: subid)[:result].values[0]

    host = fetch_ip(iplist4, :public)
    await_ssh(host)

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

        ssh_key_id:    ssh_key_id,

        id:           subid,
        created_at:   Time.now.iso8601,
        host:         host,
        ipv4_public:  host,
        ipv4_private: fetch_ip(iplist4, :private),
        ipv6_public:  fetch_ip(iplist6, :public),
        ipv6_private: fetch_ip(iplist6, :private),
        meta:         {}
    }
  end
end
sizes_map() click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 59
def sizes_map
  Vultr::Plans.list[:result].values.select do |v|
    v["plan_type"] == 'SSD'
  end.map do |v|
    if (m = v["name"].match(/^(\d+) ([MGTP]B) RAM/i))
      _, ram, unit = *m
      ram = ram.to_i

      if unit == "MB" && ram >= 1024
        ram = ram / 1024
        unit = "GB"
      end

      if unit == "GB" && ram >= 1024
        ram = ram / 1024
        unit = "TB"
      end

      ["#{ram}#{unit}".downcase, v["VPSPLANID"] ]
    else
      nil
    end
  end.compact.to_h
end
upload_ssh_key(file:) click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 89
def upload_ssh_key(file:)
  key = ssh_key_info(file: file)
  Vultr::SSHKey.create(name: "Cult: #{key[:name]}",
                       ssh_key: key[:data])[:result]["SSHKEYID"]
end
zones_map() click to toggle source
# File lib/cult/drivers/vultr_driver.rb, line 33
def zones_map
  Vultr::Regions.list[:result].map do |k, v|
    [slugify(v["regioncode"]), v["DCID"]]
  end.to_h
end