class Cult::Drivers::LinodeDriver
Constants
- SWAP_SIZE
Attributes
client[R]
Public Class Methods
interrupts()
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 212 def self.interrupts # I hate IRB. [Interrupt] + (defined?(IRB) ? [IRB::Abort] : []) end
new(api_key:)
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 52 def initialize(api_key:) LinodeMonkeyPatch.install! @client = Linode.new(api_key: api_key) end
setup!()
click to toggle source
Calls superclass method
Cult::Driver::setup!
# File lib/cult/drivers/linode_driver.rb, line 218 def self.setup! super LinodeMonkeyPatch.install! linode = nil api_key = nil begin loop do puts "Cult needs an API key. It can get one for you, but will " + "need your Linode username and password. If you'd rather " "generate it at Linode, hit ctrl-c" username = CLI.ask "Username" password = CLI.password "Password" linode = Linode.new(username: username, password: password) begin linode.fetch_api_key(label: "Cult", expires: nil) api_key = linode.api_key fail RuntimeError if api_key.nil? puts "Got it! In case you're curious: #{api_key}" rescue RuntimeError puts "Linode disagreed with your password." next if CLI.yes_no?("Try again?") end break end rescue *interrupts puts url = "https://manager.linode.com/profile/api" puts "You can obtain an API key for Cult at the following URL:" puts " #{url}" puts CLI.launch_browser(url) if CLI.yes_no?("Open Browser?") api_key = CLI.prompt("API Key") end linode ||= Linode.new(api_key: api_key) resp = linode.test.echo(message: "PING") if resp.message != 'PING' raise "Didn't respond to ping. Something went wrong." 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
Public Instance Methods
destroy!(id:, ssh_key_id: [])
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 122 def destroy!(id:, ssh_key_id: []) client.linode.delete(linodeid: id, skipchecks: true) end
disk_size_for_size(size)
click to toggle source
We try to use the reasonable sizes that the web UI uses, although the API lets us change it.
# File lib/cult/drivers/linode_driver.rb, line 97 def disk_size_for_size(size) gb = 1024 { '2gb' => 24 * gb, '4gb' => 48 * gb, '8gb' => 96 * gb, '12gb' => 192 * gb, '24gb' => 384 * gb, '48gb' => 768 * gb, '64gb' => 1152 * gb, '80gb' => 1536 * gb, '120gb' => 1920 * gb }.fetch(size.to_s) end
images_map()
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 58 def images_map client.avail.distributions.select(&:is64bit).map do |v| name = v.label [ slugify(distro_name(v.label)), v.distributionid ] end.to_h end
latest_kernel_id()
click to toggle source
I've been told by Linode support that this literal will always mean “Latest x86”. But in case that changes…
# File lib/cult/drivers/linode_driver.rb, line 115 def latest_kernel_id @latest_kernel_id ||= 138 || begin client.avail.kernels.find {|k| k.label.match(/^latest 64 bit/i)} end.kernelid end
provision!(name:, size:, zone:, image:, ssh_public_key:)
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 127 def provision!(name:, size:, zone:, image:, ssh_public_key:) 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) disksize = disk_size_for_size(size) transaction do |xac| linodeid = client.linode.create(datacenterid: zoneid, planid: sizeid).linodeid xac.rollback do destroy!(id: linodeid) end # We give it a name early so we can find it in the Web UI if anything # goes wrong. client.linode.update(linodeid: linodeid, label: name) client.linode.ip.addprivate(linodeid: linodeid) # You shouldn't run meaningful swap, but this makes the Web UI not # scare you, and apparently Linux runs better with ANY swap, # regardless of how small. We've matched the small size the Linode # Web UI does by default. swapid = client.linode.disk.create(linodeid: linodeid, label: "Cult: #{name}-swap", type: "swap", size: SWAP_SIZE).diskid # Here, we create the OS on-node storage params = { linodeid: linodeid, distributionid: imageid, label: "Cult: #{name}", # Linode's max length is 128, generates longer than that to # no get the fixed == and truncates. rootpass: SecureRandom.base64(100)[0...128], rootsshkey: ssh_key_info(file: ssh_public_key)[:data], size: disksize - SWAP_SIZE } diskid = client.linode.disk.createfromdistribution(params).diskid # We don't have to reference the config specifically: It'll be the # only configuration that exists, so it'll be used. client.linode.config.create(linodeid: linodeid, kernelid: latest_kernel_id, disklist: "#{diskid},#{swapid}", rootdevicenum: 1, label: "Cult: Latest Linux-x64") client.linode.reboot(linodeid: linodeid) # Information gathering step... all_ips = client.linode.ip.list(linodeid: linodeid) ipv4_public = all_ips.find{ |ip| ip.ispublic == 1 }&.ipaddress ipv4_private = all_ips.find{ |ip| ip.ispublic == 0 }&.ipaddress # This is a shame: Linode has awesome support for ipv6, but doesn't # expose it in the API. ipv6_public = nil ipv6_private = nil await_ssh(ipv4_public) return { name: name, size: size, zone: zone, image: image, id: linodeid, created_at: Time.now.iso8601, host: ipv4_public, ipv4_public: ipv4_public, ipv4_private: ipv4_private, ipv6_public: ipv6_public, ipv6_private: ipv6_private, meta: {} } end end
sizes_map()
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 77 def sizes_map client.avail.linodeplans.map do |v| name = v.label.gsub(/^Linode /, '') if name.match(/^\d+$/) mb = name.to_i if mb < 1024 "#{mb}mb" else name = "#{mb / 1024}gb" end end [ slugify(name), v.planid ] end.to_h end
zones_map()
click to toggle source
# File lib/cult/drivers/linode_driver.rb, line 68 def zones_map client.avail.datacenters.map do |v| [ slugify(v.abbr), v.datacenterid ] end.to_h end