Class: Cult::Drivers::VultrDriver

Inherits:
Cult::Driver show all
Defined in:
lib/cult/drivers/vultr_driver.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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:) ⇒ VultrDriver

Returns a new instance of VultrDriver.



11
12
13
# File 'lib/cult/drivers/vultr_driver.rb', line 11

def initialize(api_key:)
  @api_key = api_key
end

Instance Attribute Details

#api_keyObject (readonly)

Returns the value of attribute api_key.



9
10
11
# File 'lib/cult/drivers/vultr_driver.rb', line 9

def api_key
  @api_key
end

Class Method Details

.setup!Object



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
# 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) ⇒ Object

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



18
19
20
21
22
23
24
25
26
27
28
29
30
# 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

Instance Method Details

#destroy!(id:, ssh_key_id: nil) ⇒ Object



104
105
106
107
# 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:) ⇒ Object



110
111
112
# 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) ⇒ Object



97
98
99
100
101
# 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_mapObject



43
44
45
46
47
48
49
50
51
52
53
# 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:) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
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
# 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_mapObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 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:) ⇒ Object



89
90
91
92
93
# 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_mapObject



33
34
35
36
37
# 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