cult 0.1.3.pre → 0.1.4.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +53 -47
- data/cult.gemspec +6 -5
- data/exe/cult +19 -4
- data/lib/cult/cli/common.rb +1 -2
- data/lib/cult/cli/console_cmd.rb +2 -2
- data/lib/cult/cli/cri_extensions.rb +66 -13
- data/lib/cult/cli/init_cmd.rb +6 -7
- data/lib/cult/cli/node_cmd.rb +233 -67
- data/lib/cult/cli/provider_cmd.rb +16 -13
- data/lib/cult/cli/role_cmd.rb +25 -26
- data/lib/cult/cli/task_cmd.rb +13 -13
- data/lib/cult/commander.rb +53 -17
- data/lib/cult/commander_sync.rb +29 -0
- data/lib/cult/definition.rb +21 -49
- data/lib/cult/driver.rb +1 -1
- data/lib/cult/drivers/common.rb +12 -11
- data/lib/cult/drivers/digital_ocean_driver.rb +2 -2
- data/lib/cult/drivers/virtual_box_driver.rb +156 -0
- data/lib/cult/drivers/vultr_driver.rb +3 -3
- data/lib/cult/named_array.rb +103 -15
- data/lib/cult/node.rb +139 -12
- data/lib/cult/paramap.rb +209 -0
- data/lib/cult/project.rb +2 -17
- data/lib/cult/provider.rb +3 -1
- data/lib/cult/role.rb +12 -8
- data/lib/cult/task.rb +73 -45
- data/lib/cult/template.rb +3 -4
- data/lib/cult/transaction.rb +11 -5
- data/lib/cult/user_refinements.rb +1 -1
- data/lib/cult/version.rb +1 -1
- data/lib/cult.rb +32 -3
- data/skel/roles/{all → base}/role.json +0 -0
- data/skel/roles/{all/tasks/00000-do-something-cool → base/tasks/000-do-something-cool} +0 -0
- data/skel/roles/{all/tasks/sync → base/tasks/sync-host-map} +5 -5
- data/skel/roles/base/tasks/sync-leader-of +11 -0
- data/skel/roles/bootstrap/files/cult-motd +15 -3
- data/skel/roles/bootstrap/tasks/{00000-set-hostname → 000-set-hostname} +1 -1
- data/skel/roles/bootstrap/tasks/{00001-add-cult-user → 001-add-cult-user} +0 -6
- data/skel/roles/bootstrap/tasks/002-disable-root-user +7 -0
- data/skel/roles/bootstrap/tasks/{00002-install-cult-motd → 002-install-cult-motd} +1 -1
- metadata +29 -11
- data/lib/cult/cli/fleet_cmd.rb +0 -37
data/lib/cult/cli/task_cmd.rb
CHANGED
@@ -6,7 +6,7 @@ module Cult
|
|
6
6
|
optional_project
|
7
7
|
name 'task'
|
8
8
|
aliases 'tasks'
|
9
|
-
summary 'Task
|
9
|
+
summary 'Task manipulation'
|
10
10
|
usage 'task [command]'
|
11
11
|
description <<~EOD.format_description
|
12
12
|
Tasks are basically shell scripts. Or anything with a \#! line, or
|
@@ -31,8 +31,9 @@ module Cult
|
|
31
31
|
neatly line these up for you.
|
32
32
|
EOD
|
33
33
|
|
34
|
-
run(arguments:
|
34
|
+
run(arguments: none) do |opts, args, cmd|
|
35
35
|
puts cmd.help
|
36
|
+
exit
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
@@ -42,10 +43,10 @@ module Cult
|
|
42
43
|
aliases 'reserial'
|
43
44
|
summary 'Resequences task serial numbers'
|
44
45
|
|
45
|
-
flag :A, :all,
|
46
|
-
flag :G, :'git-add',
|
47
|
-
required :r, :role,
|
48
|
-
|
46
|
+
flag :A, :all, 'Re-sequence all roles'
|
47
|
+
flag :G, :'git-add', '`git add` each change'
|
48
|
+
required :r, :role, 'Resequence only /NODE+/ (multiple)',
|
49
|
+
multiple: true
|
49
50
|
|
50
51
|
description <<~EOD.format_description
|
51
52
|
Resequences the serial numbers in each task provided with --roles,
|
@@ -65,7 +66,7 @@ module Cult
|
|
65
66
|
EOD
|
66
67
|
|
67
68
|
|
68
|
-
run(arguments:
|
69
|
+
run(arguments: none) do |opts, args, cmd|
|
69
70
|
if opts[:all] && Array(opts[:role]).size != 0
|
70
71
|
fail CLIError, "can't supply -A and also a list of roles"
|
71
72
|
end
|
@@ -131,24 +132,23 @@ module Cult
|
|
131
132
|
task.add_command task_sanity
|
132
133
|
|
133
134
|
|
134
|
-
|
135
|
-
name '
|
136
|
-
aliases 'new'
|
135
|
+
task_new = Cri::Command.define do
|
136
|
+
name 'new'
|
137
137
|
usage 'create [options] DESCRIPTION'
|
138
138
|
summary 'create a new task for ROLE with a proper serial'
|
139
139
|
description <<~EOD.format_description
|
140
140
|
EOD
|
141
141
|
|
142
|
-
required :r, :role, '
|
142
|
+
required :r, :role, '/ROLE/ for task. defaults to "base"'
|
143
143
|
flag :e, :edit, 'open generated task file in your $EDITOR'
|
144
144
|
|
145
145
|
run do |opts, args, cmd|
|
146
146
|
english = args.join " "
|
147
|
-
opts[:roles] ||= '
|
147
|
+
opts[:roles] ||= 'base'
|
148
148
|
puts [english, opts[:roles], opts[:edit]].inspect
|
149
149
|
end
|
150
150
|
end
|
151
|
-
task.add_command
|
151
|
+
task.add_command(task_new)
|
152
152
|
|
153
153
|
task
|
154
154
|
end
|
data/lib/cult/commander.rb
CHANGED
@@ -18,6 +18,7 @@ module Cult
|
|
18
18
|
Shellwords.escape(s)
|
19
19
|
end
|
20
20
|
|
21
|
+
|
21
22
|
def send_tar(io, ssh)
|
22
23
|
filename = SecureRandom.hex + ".tar"
|
23
24
|
puts "Uploading bundle: #{filename}"
|
@@ -26,6 +27,7 @@ module Cult
|
|
26
27
|
ssh.exec! "tar -xf #{esc(filename)} && rm #{esc(filename)}"
|
27
28
|
end
|
28
29
|
|
30
|
+
|
29
31
|
def create_build_tar(role)
|
30
32
|
io = StringIO.new
|
31
33
|
Bundle.new(io) do |bundle|
|
@@ -41,11 +43,12 @@ module Cult
|
|
41
43
|
io
|
42
44
|
end
|
43
45
|
|
46
|
+
|
44
47
|
def exec_remote!(ssh:, role:, task:)
|
45
48
|
token = SecureRandom.hex
|
46
49
|
task_bin = role.relative_path(task.path)
|
47
50
|
|
48
|
-
puts "Executing: #{task.remote_path}"
|
51
|
+
puts "Executing: #{task.remote_path} on #{node.name}"
|
49
52
|
res = ssh.exec! <<~BASH
|
50
53
|
cd #{esc(role.remote_path)}; \
|
51
54
|
./#{esc(task_bin)} && \
|
@@ -63,6 +66,7 @@ module Cult
|
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
69
|
+
|
66
70
|
def install!(role)
|
67
71
|
connect(user: role.user) do |ssh|
|
68
72
|
io = create_build_tar(role)
|
@@ -77,20 +81,28 @@ module Cult
|
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
80
|
-
|
84
|
+
|
85
|
+
def find_sync_tasks(pass:, roles: nil)
|
86
|
+
selected_roles = node.build_order
|
87
|
+
|
88
|
+
if roles
|
89
|
+
selected_roles.select! { |r| roles.include?(r) }
|
90
|
+
end
|
91
|
+
|
81
92
|
r = []
|
82
|
-
|
83
|
-
role.event_tasks.
|
84
|
-
|
93
|
+
selected_roles.each do |role|
|
94
|
+
r += role.event_tasks.select do |t|
|
95
|
+
t.event == :sync && t.pass == pass
|
85
96
|
end
|
86
97
|
end
|
87
98
|
r
|
88
99
|
end
|
89
100
|
|
90
|
-
|
101
|
+
|
102
|
+
def create_sync_tar(pass:, roles: nil)
|
91
103
|
io = StringIO.new
|
92
104
|
Bundle.new(io) do |bundle|
|
93
|
-
find_sync_tasks.each do |task|
|
105
|
+
find_sync_tasks(pass: pass, roles: roles).each do |task|
|
94
106
|
bundle.add_file(project, task.role, node, task)
|
95
107
|
end
|
96
108
|
end
|
@@ -99,29 +111,53 @@ module Cult
|
|
99
111
|
io
|
100
112
|
end
|
101
113
|
|
102
|
-
|
114
|
+
|
115
|
+
def sync!(pass:, roles: nil)
|
116
|
+
io = create_sync_tar(pass: pass, roles: roles)
|
117
|
+
return if io.eof?
|
118
|
+
|
103
119
|
connect do |ssh|
|
104
|
-
io = create_sync_tar
|
105
120
|
send_tar(io, ssh)
|
106
|
-
find_sync_tasks.each do |task|
|
121
|
+
find_sync_tasks(pass: pass, roles: roles).each do |task|
|
107
122
|
exec_remote!(ssh: ssh, role: task.role, task: task)
|
108
123
|
end
|
109
124
|
end
|
110
125
|
end
|
111
126
|
|
127
|
+
|
112
128
|
def bootstrap!
|
113
129
|
bootstrap_role = CLI.fetch_item('bootstrap', from: Role)
|
114
130
|
install!(bootstrap_role)
|
115
131
|
end
|
116
132
|
|
133
|
+
def ping
|
134
|
+
connect do |ssh|
|
135
|
+
ssh.exec! "uptime"
|
136
|
+
end
|
137
|
+
rescue
|
138
|
+
nil
|
139
|
+
end
|
140
|
+
|
117
141
|
def connect(user: nil, &block)
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
142
|
+
5.times do |attempt|
|
143
|
+
begin
|
144
|
+
user ||= node.user
|
145
|
+
puts "Connecting with user=#{user}, host=#{node.host}, " +
|
146
|
+
"key=#{node.ssh_private_key_file}"
|
147
|
+
Net::SSH.start(node.host,
|
148
|
+
user,
|
149
|
+
port: node.ssh_port,
|
150
|
+
user_known_hosts_file: node.ssh_known_hosts_file,
|
151
|
+
timeout: 5,
|
152
|
+
auth_methods: ['publickey'],
|
153
|
+
keys_only: true,
|
154
|
+
keys: [node.ssh_private_key_file]) do |ssh|
|
155
|
+
return (yield ssh)
|
156
|
+
end
|
157
|
+
rescue Errno::ECONNREFUSED, Net::SSH::ConnectionTimeout
|
158
|
+
puts "Connection refused. Retrying"
|
159
|
+
sleep attempt * 3
|
160
|
+
end
|
125
161
|
end
|
126
162
|
end
|
127
163
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Cult
|
2
|
+
class CommanderSync
|
3
|
+
attr_reader :project, :nodes
|
4
|
+
def initialize(project:, nodes:)
|
5
|
+
@project, @nodes = project, nodes
|
6
|
+
end
|
7
|
+
|
8
|
+
def sync!(roles: nil, passes: nil)
|
9
|
+
roles ||= Cult.project.roles
|
10
|
+
passes ||= required_passes(roles)
|
11
|
+
|
12
|
+
passes.each do |pass|
|
13
|
+
puts Rainbow("Executing pass #{pass}").yellow
|
14
|
+
Cult.paramap(nodes) do |node|
|
15
|
+
c = Commander.new(project: project, node: node)
|
16
|
+
c.sync!(pass: pass, roles: roles)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def required_passes(roles)
|
22
|
+
# searches through every node and extracts which passes have to be ran
|
23
|
+
# to satisfy every event task
|
24
|
+
nodes.map(&:build_order).flatten.uniq
|
25
|
+
.select { |r| roles.nil? ? true : roles.include?(r) }
|
26
|
+
.map(&:event_tasks).flatten.map(&:pass).uniq.sort
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/cult/definition.rb
CHANGED
@@ -1,83 +1,55 @@
|
|
1
|
-
require 'yaml'
|
2
1
|
require 'json'
|
3
|
-
require 'forwardable'
|
4
2
|
|
5
3
|
module Cult
|
6
4
|
class Definition
|
7
5
|
attr_reader :object
|
8
6
|
attr_reader :bag
|
9
7
|
|
10
|
-
extend Forwardable
|
11
|
-
def_delegators :object, :definition_parameters,
|
12
|
-
:definition_path,
|
13
|
-
:definition_parents
|
14
|
-
|
15
8
|
def initialize(object)
|
16
9
|
@object = object
|
17
10
|
end
|
18
11
|
|
12
|
+
def definition_parameters
|
13
|
+
object.definition_parameters
|
14
|
+
end
|
15
|
+
|
16
|
+
def definition_path
|
17
|
+
object.definition_path
|
18
|
+
end
|
19
|
+
|
20
|
+
def definition_parents
|
21
|
+
object.definition_parents
|
22
|
+
end
|
23
|
+
|
19
24
|
def inspect
|
20
25
|
"\#<#{self.class.name} " +
|
21
26
|
"object: #{object.inspect}, " +
|
22
27
|
"params: #{definition_parameters}, " +
|
23
28
|
"parents: #{definition_parents}, " +
|
24
|
-
"
|
29
|
+
"bag: #{bag}>"
|
25
30
|
end
|
26
31
|
alias_method :to_s, :inspect
|
27
32
|
|
28
|
-
|
29
|
-
def filenames
|
30
|
-
Array(definition_path).map do |dp|
|
31
|
-
attempt = [ "#{dp}.yaml", "#{dp}.yml", "#{dp}.json" ]
|
32
|
-
existing = attempt.select do |filename|
|
33
|
-
File.exist?(filename)
|
34
|
-
end
|
35
|
-
if existing.size > 1
|
36
|
-
raise RuntimeError, "conflicting definition files: #{existing}"
|
37
|
-
end
|
38
|
-
existing[0]
|
39
|
-
end.compact
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
def decoder_for(filename)
|
44
|
-
@decoder_for ||= begin
|
45
|
-
case filename
|
46
|
-
when nil
|
47
|
-
nil
|
48
|
-
when /\.json\z/
|
49
|
-
JSON.method(:parse)
|
50
|
-
when /\.ya?ml\z/
|
51
|
-
YAML.method(:safe_load)
|
52
|
-
else
|
53
|
-
fail RuntimeError, "No decoder for file type: #{filename}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
33
|
def bag
|
60
|
-
@bag ||=
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
67
|
-
result
|
34
|
+
@bag ||= Array(definition_path).select do |filename|
|
35
|
+
File.exist?(filename)
|
36
|
+
end.inject({}) do |acc, filename|
|
37
|
+
erb = ::Cult::Template.new(project: nil, **definition_parameters)
|
38
|
+
contents = erb.process(File.read(filename), filename: filename)
|
39
|
+
JSON.parse(contents).merge(acc)
|
68
40
|
end
|
69
41
|
end
|
70
42
|
alias_method :to_h, :bag
|
71
43
|
|
72
44
|
|
73
45
|
def direct(k)
|
74
|
-
fail
|
46
|
+
fail ArgumentError unless k.is_a?(String)
|
75
47
|
bag[k]
|
76
48
|
end
|
77
49
|
|
78
50
|
|
79
51
|
def [](k)
|
80
|
-
fail
|
52
|
+
fail ArgumentError unless k.is_a?(String)
|
81
53
|
if bag.key?(k)
|
82
54
|
bag[k]
|
83
55
|
else
|
data/lib/cult/driver.rb
CHANGED
data/lib/cult/drivers/common.rb
CHANGED
@@ -90,7 +90,8 @@ module Cult
|
|
90
90
|
|
91
91
|
# We don't particularly need the debian codename
|
92
92
|
s = s.gsub(/(\d)[\s-]+(\S+)/, '\1') if s.match(/^debian/i)
|
93
|
-
s
|
93
|
+
s = s.gsub(/[\s.]+/, '-')
|
94
|
+
s.downcase
|
94
95
|
end
|
95
96
|
|
96
97
|
|
@@ -100,14 +101,12 @@ module Cult
|
|
100
101
|
times = 0
|
101
102
|
total_wait = 0.0
|
102
103
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
wait *= scale
|
110
|
-
end
|
104
|
+
loop do
|
105
|
+
yield times, total_wait
|
106
|
+
sleep wait
|
107
|
+
times += 1
|
108
|
+
total_wait += wait
|
109
|
+
wait *= scale
|
111
110
|
end
|
112
111
|
end
|
113
112
|
|
@@ -115,11 +114,13 @@ module Cult
|
|
115
114
|
# Waits until SSH is available at host. "available" jsut means
|
116
115
|
# "listening"/acceping connections.
|
117
116
|
def await_ssh(host)
|
117
|
+
puts "Awaiting sshd on #{host}"
|
118
118
|
backoff_loop do
|
119
119
|
begin
|
120
120
|
sock = connect_timeout(host, 22, 1)
|
121
|
-
|
122
|
-
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED
|
121
|
+
break
|
122
|
+
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
|
123
|
+
Errno::EHOSTDOWN
|
123
124
|
# Nothing, these are expected
|
124
125
|
ensure
|
125
126
|
sock.close if sock
|
@@ -58,7 +58,7 @@ module Cult
|
|
58
58
|
d = nil
|
59
59
|
backoff_loop do
|
60
60
|
d = client.droplets.find(id: droplet.id)
|
61
|
-
|
61
|
+
break if d.status == 'active'
|
62
62
|
end
|
63
63
|
return d
|
64
64
|
end
|
@@ -77,7 +77,7 @@ module Cult
|
|
77
77
|
transaction do |xac|
|
78
78
|
ssh_key_id = upload_ssh_key(file: ssh_public_key)
|
79
79
|
xac.rollback do
|
80
|
-
destroy_ssh_key!(
|
80
|
+
destroy_ssh_key!(ssh_key_id: ssh_key_id)
|
81
81
|
end
|
82
82
|
|
83
83
|
begin
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Cult
|
4
|
+
module Drivers
|
5
|
+
class VirtualBoxDriver < ::Cult::Driver
|
6
|
+
|
7
|
+
def initialize(api_key:)
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def sizes_map
|
12
|
+
[1, 2, 4, 6, 8, 10, 12, 16].map do |size|
|
13
|
+
["#{size}gb", size * 1024 * 1024]
|
14
|
+
end.to_h
|
15
|
+
end
|
16
|
+
with_id_mapping :sizes_map
|
17
|
+
|
18
|
+
|
19
|
+
def images_map
|
20
|
+
%x(VBoxManage list vms).each_line.map do |line|
|
21
|
+
words = Shellwords.split(line.chomp)
|
22
|
+
[ distro_name(words[0]), words[1] ]
|
23
|
+
end.to_h
|
24
|
+
end
|
25
|
+
with_id_mapping :images_map
|
26
|
+
|
27
|
+
|
28
|
+
def zones
|
29
|
+
['local']
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def ip_property_name(index, protocol)
|
34
|
+
protocols = {
|
35
|
+
ipv4: 'V4',
|
36
|
+
ipv6: 'V6'
|
37
|
+
}
|
38
|
+
"/VirtualBox/GuestInfo/Net/#{index}/#{protocols[protocol]}/IP"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def esc(s)
|
43
|
+
Shellwords.escape(s)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def unset_ip_data(name, index, protocol)
|
48
|
+
cmd = "VBoxManage guestproperty unset #{esc(name)} " +
|
49
|
+
"#{ip_property_name(index, protocol)}"
|
50
|
+
`#{cmd}`
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def get_ip_data(name, index, protocol)
|
55
|
+
cmd = "VBoxManage guestproperty get #{esc(name)} " +
|
56
|
+
"#{ip_property_name(index, protocol)}"
|
57
|
+
s = `#{cmd}`
|
58
|
+
|
59
|
+
if $?.success? && (m = s.match(/^Value: (.+)$/))
|
60
|
+
m[1]
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def await_ip_address(name, index, protocol)
|
68
|
+
puts "Awaiting IP address from VirtualBox Guest Additions"
|
69
|
+
unset_ip_data(name, index, protocol)
|
70
|
+
|
71
|
+
backoff_loop do
|
72
|
+
if (ip = get_ip_data(name, index, protocol))
|
73
|
+
return ip
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def destroy!(id:, ssh_key_id:)
|
80
|
+
system 'VBoxManage', 'controlvm', id, 'poweroff'
|
81
|
+
system 'VBoxManage', 'unregistervm', id, '--delete'
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def guest_copy(name, src, dst)
|
86
|
+
cmd = "VBoxManage guestcontrol #{esc(name)} " +
|
87
|
+
"--username root --password password " +
|
88
|
+
"copyto #{esc(src)} --target-directory #{esc(dst)}"
|
89
|
+
puts cmd
|
90
|
+
`#{cmd}`
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def guest_command(name, cmd)
|
95
|
+
cmd = "VBoxManage guestcontrol #{esc(name)} " +
|
96
|
+
"--username root --password password " +
|
97
|
+
"run -- /bin/sh -c #{esc(cmd)}"
|
98
|
+
puts cmd
|
99
|
+
`#{cmd}`
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def provision!(name:, size:, zone:, image:, ssh_public_key:)
|
104
|
+
system 'VBoxManage', 'clonevm',
|
105
|
+
fetch_mapped(name: :image, from: images_map, key: image),
|
106
|
+
'--name', name, '--register'
|
107
|
+
|
108
|
+
system 'VBoxManage', 'modifyvm', name, '--groups', '/Cult'
|
109
|
+
system 'VBoxManage', 'startvm', name, '--type', 'headless'
|
110
|
+
|
111
|
+
public_ip = await_ip_address(name, 0, :ipv4)
|
112
|
+
private_ip = public_ip
|
113
|
+
|
114
|
+
await_ssh(public_ip)
|
115
|
+
|
116
|
+
guest_command(name, "mkdir -m 0600 /root/.ssh")
|
117
|
+
guest_copy(name, ssh_public_key, "/root/.ssh/authorized_keys")
|
118
|
+
guest_command(name, "chmod 0644 /root/.ssh/authorized_keys")
|
119
|
+
guest_command(name, "passwd -l root")
|
120
|
+
|
121
|
+
return {
|
122
|
+
name: name,
|
123
|
+
size: size,
|
124
|
+
zone: zone,
|
125
|
+
image: image,
|
126
|
+
|
127
|
+
id: name,
|
128
|
+
created_at: Time.now.iso8601,
|
129
|
+
host: public_ip,
|
130
|
+
ipv4_public: public_ip,
|
131
|
+
ipv4_private: private_ip,
|
132
|
+
ipv6_public: nil,
|
133
|
+
ipv6_private: nil,
|
134
|
+
meta: {}
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.setup!
|
139
|
+
super
|
140
|
+
|
141
|
+
inst = new(api_key: nil)
|
142
|
+
|
143
|
+
return {
|
144
|
+
driver: driver_name,
|
145
|
+
api_key: nil,
|
146
|
+
configurations: {
|
147
|
+
sizes: inst.sizes,
|
148
|
+
zones: inst.zones,
|
149
|
+
images: inst.images,
|
150
|
+
}
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -31,7 +31,7 @@ module Cult
|
|
31
31
|
|
32
32
|
|
33
33
|
def zones_map
|
34
|
-
Vultr::
|
34
|
+
Vultr::Regions.list[:result].map do |k, v|
|
35
35
|
[slugify(v["regioncode"]), v["DCID"]]
|
36
36
|
end.to_h
|
37
37
|
end
|
@@ -57,7 +57,7 @@ module Cult
|
|
57
57
|
|
58
58
|
|
59
59
|
def sizes_map
|
60
|
-
Vultr::
|
60
|
+
Vultr::Plans.list[:result].values.select do |v|
|
61
61
|
v["plan_type"] == 'SSD'
|
62
62
|
end.map do |v|
|
63
63
|
if (m = v["name"].match(/^(\d+) ([MGTP]B) RAM/i))
|
@@ -141,7 +141,7 @@ module Cult
|
|
141
141
|
# Wait until it's active, it won't have an IP until then
|
142
142
|
backoff_loop do
|
143
143
|
r = Vultr::Server.list(SUBID: subid)[:result]
|
144
|
-
|
144
|
+
break if r['status'] == 'active'
|
145
145
|
end
|
146
146
|
|
147
147
|
iplist4 = Vultr::Server.list_ipv4(SUBID: subid)[:result].values[0]
|