Class: Cult::Drivers::LinodeDriver
- Inherits:
-
Cult::Driver
- Object
- Cult::Driver
- Cult::Drivers::LinodeDriver
- Defined in:
- lib/cult/drivers/linode_driver.rb
Constant Summary collapse
- SWAP_SIZE =
256
Instance Attribute Summary collapse
-
#client ⇒ Object
readonly
Returns the value of attribute client.
Class Method Summary collapse
Instance Method Summary collapse
- #destroy!(id:, ssh_key_id: []) ⇒ Object
-
#disk_size_for_size(size) ⇒ Object
We try to use the reasonable sizes that the web UI uses, although the API lets us change it.
- #images_map ⇒ Object
-
#initialize(api_key:) ⇒ LinodeDriver
constructor
A new instance of LinodeDriver.
-
#latest_kernel_id ⇒ Object
I’ve been told by Linode support that this literal will always mean “Latest x86”.
- #provision!(name:, size:, zone:, image:, ssh_public_key:) ⇒ Object
- #sizes_map ⇒ Object
- #zones_map ⇒ Object
Methods inherited from Cult::Driver
driver_name, inspect, #inspect, named_array_identifier, new, to_s, #to_s, try_requires!
Methods included from Common
#await_ssh, #backoff_loop, #connect_timeout, #distro_name, #fetch_mapped, included, #slugify, #ssh_key_info
Constructor Details
#initialize(api_key:) ⇒ LinodeDriver
Returns a new instance of LinodeDriver.
52 53 54 55 |
# File 'lib/cult/drivers/linode_driver.rb', line 52 def initialize(api_key:) LinodeMonkeyPatch.install! @client = Linode.new(api_key: api_key) end |
Instance Attribute Details
#client ⇒ Object (readonly)
Returns the value of attribute client.
50 51 52 |
# File 'lib/cult/drivers/linode_driver.rb', line 50 def client @client end |
Class Method Details
.interrupts ⇒ Object
212 213 214 215 |
# File 'lib/cult/drivers/linode_driver.rb', line 212 def self.interrupts # I hate IRB. [Interrupt] + (defined?(IRB) ? [IRB::Abort] : []) end |
.setup! ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# 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. != '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 |
Instance Method Details
#destroy!(id:, ssh_key_id: []) ⇒ Object
122 123 124 |
# 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) ⇒ Object
We try to use the reasonable sizes that the web UI uses, although the API lets us change it.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# 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 ⇒ Object
58 59 60 61 62 63 |
# 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 ⇒ Object
I’ve been told by Linode support that this literal will always mean “Latest x86”. But in case that changes…
115 116 117 118 119 |
# 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:) ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 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 207 208 209 |
# 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 ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# 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 ⇒ Object
68 69 70 71 72 |
# 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 |