asteroid 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/Gemfile +1 -0
- data/README.md +45 -4
- data/Rakefile +1 -0
- data/asteroid.gemspec +0 -1
- data/bin/asteroid +4 -2
- data/lib/asteroid.rb +11 -10
- data/lib/asteroid/application.rb +60 -7
- data/lib/asteroid/config.rb +28 -14
- data/lib/asteroid/file_reference.rb +102 -0
- data/lib/asteroid/generator.rb +0 -4
- data/lib/asteroid/instance.rb +29 -9
- data/lib/asteroid/instance/command.rb +71 -36
- data/lib/asteroid/instance/scp.rb +26 -0
- data/lib/asteroid/instance/ssh.rb +2 -1
- data/lib/asteroid/instance/vars.rb +5 -2
- data/lib/asteroid/key_reference.rb +45 -0
- data/lib/asteroid/provider/abstract.rb +16 -1
- data/lib/asteroid/provider/digital_ocean.rb +35 -6
- data/lib/asteroid/provider/mock.rb +36 -14
- data/lib/asteroid/script.rb +3 -57
- data/lib/asteroid/server.rb +44 -25
- data/lib/asteroid/template.rb +19 -7
- data/lib/asteroid/template/liquid.rb +0 -0
- data/lib/asteroid/version.rb +1 -1
- data/sample/.gitignore +2 -0
- data/sample/README.md +1 -0
- data/sample/Rakefile +0 -0
- data/sample/TODO +9 -0
- data/sample/asteroid/files/nginx/available_site.conf.erb +0 -0
- data/sample/asteroid/files/nginx/nginx.conf +1 -0
- data/sample/asteroid/scripts/ubuntu.aster +4 -0
- data/sample/asteroid/servers/default.yml +3 -0
- data/sample/asteroid/servers/web.yml +5 -0
- data/sample/config/asteroid.rb +9 -0
- data/test/helper.rb +18 -2
- data/test/unit/test_application.rb +17 -0
- data/test/unit/test_file_reference.rb +18 -0
- data/test/unit/test_instance.rb +20 -4
- data/test/unit/test_script_reference.rb +9 -0
- data/test/unit/test_server.rb +46 -2
- data/test/unit/test_template.rb +16 -0
- metadata +24 -17
- data/lib/asteroid/config_file.rb +0 -46
@@ -2,33 +2,39 @@
|
|
2
2
|
module Asteroid
|
3
3
|
class Instance
|
4
4
|
|
5
|
-
def
|
6
|
-
env
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
5
|
+
def render_erb(string, env)
|
6
|
+
Template.new(:erb, string).render(env)
|
7
|
+
end
|
8
|
+
|
9
|
+
def eval_command(cmd)
|
10
|
+
|
11
|
+
|
12
|
+
return aster_environment.eval(cmd)
|
13
|
+
|
14
|
+
# env ||= template_data
|
15
|
+
# cmd = Template.new(:erb, cmd).render(env)
|
16
|
+
# cmd, *rest = cmd.split(" ")
|
17
|
+
# case cmd.to_sym
|
18
|
+
# when :run
|
19
|
+
# command_run(rest.first, env)
|
20
|
+
# when :upload
|
21
|
+
# from, to, _ = rest
|
22
|
+
# command_upload(from, to, env)
|
23
|
+
# when :"upload-private-key"
|
24
|
+
# name, _ = rest
|
25
|
+
# command_upload_private_key(name, env)
|
26
|
+
# when :exec
|
27
|
+
# script = rest.join(" ")
|
28
|
+
# command_exec(script, env)
|
29
|
+
# when :config
|
30
|
+
# set_or_get, var, val, _ = rest
|
31
|
+
# command_config(set_or_get, var, val)
|
32
|
+
# else
|
33
|
+
# if server.commands[cmd]
|
34
|
+
# command_command(cmd, rest, env)
|
35
|
+
# else
|
36
|
+
# end
|
37
|
+
# end
|
32
38
|
end
|
33
39
|
|
34
40
|
def template_data
|
@@ -38,6 +44,7 @@ module Asteroid
|
|
38
44
|
end
|
39
45
|
|
40
46
|
def command_command(cmd, args, env)
|
47
|
+
raise "Not here"
|
41
48
|
env ||= template_data
|
42
49
|
command = server.commands[cmd]
|
43
50
|
|
@@ -61,7 +68,7 @@ module Asteroid
|
|
61
68
|
env ||= template_data
|
62
69
|
filename = File.join(Asteroid::Config.script_dir, '/', yml_script)
|
63
70
|
script = File.read(filename)
|
64
|
-
yml = Template.new(:erb).render(
|
71
|
+
yml = Template.new(:erb, script).render(env)
|
65
72
|
data = YAML::load yml
|
66
73
|
unless data["steps"]
|
67
74
|
raise "No steps in #{filenbame}"
|
@@ -74,12 +81,36 @@ module Asteroid
|
|
74
81
|
|
75
82
|
def aster_environment
|
76
83
|
@aster_environment ||= Aster::Environment.new.tap do |e|
|
77
|
-
|
78
|
-
|
84
|
+
|
85
|
+
self.server.commands.each_pair do |name, data|
|
86
|
+
data = case data
|
87
|
+
when Array
|
88
|
+
{args: [], steps: data}
|
89
|
+
when Object
|
90
|
+
data
|
91
|
+
else
|
92
|
+
raise "Commands should be Arrays or arguments or Objects"
|
93
|
+
end
|
94
|
+
|
95
|
+
commands = Aster::Parser.new.send :parse_lines, data[:steps]
|
96
|
+
e.define_function name, data[:args], commands
|
79
97
|
end
|
80
98
|
|
81
|
-
|
82
|
-
|
99
|
+
|
100
|
+
e.define_function :run, [:"..."] do |arguments|
|
101
|
+
command_run(arguments.join(' '))
|
102
|
+
end
|
103
|
+
|
104
|
+
e.define_function :upload, [:"..."] do |(from, to, _)|
|
105
|
+
if command_upload from, to
|
106
|
+
"yes"
|
107
|
+
else
|
108
|
+
"no"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
e.define_function :exec, [:"..."] do |arguments|
|
113
|
+
command_exec(arguments.join(' '))
|
83
114
|
end
|
84
115
|
end
|
85
116
|
end
|
@@ -94,6 +125,9 @@ module Asteroid
|
|
94
125
|
env ||= template_data
|
95
126
|
script = Script.named(script_name)
|
96
127
|
|
128
|
+
# touch ssh
|
129
|
+
self.ssh
|
130
|
+
|
97
131
|
if script.nil?
|
98
132
|
raise "No script named #{script_name}"
|
99
133
|
end
|
@@ -120,14 +154,15 @@ module Asteroid
|
|
120
154
|
|
121
155
|
def command_upload(from, to, env = nil)
|
122
156
|
env ||= template_data
|
123
|
-
from =
|
157
|
+
from = FileReference.new(from)
|
124
158
|
|
125
159
|
if from.template?
|
126
|
-
from.
|
160
|
+
from.data = env
|
127
161
|
end
|
128
162
|
|
129
|
-
puts from.
|
130
|
-
|
163
|
+
puts from.rendered_filename
|
164
|
+
|
165
|
+
self.scp_upload from.rendered_filename, to
|
131
166
|
end
|
132
167
|
|
133
168
|
def command_exec(command, env = nil)
|
@@ -1,8 +1,34 @@
|
|
1
1
|
require 'net/scp'
|
2
2
|
|
3
3
|
module Asteroid
|
4
|
+
|
5
|
+
class MockSCP
|
6
|
+
|
7
|
+
def initialize(fs)
|
8
|
+
@fs = fs || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def upload!(from, to)
|
12
|
+
@fs[to] = File.read(from)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
4
17
|
class Instance
|
18
|
+
|
19
|
+
def scp_upload(from, to)
|
20
|
+
scp.upload! from, to
|
21
|
+
end
|
22
|
+
|
23
|
+
def mock_file_system
|
24
|
+
@mock_file_system ||= {}
|
25
|
+
end
|
26
|
+
|
5
27
|
def scp
|
28
|
+
if server.provider.type == :mock
|
29
|
+
return @mock_scp ||= MockSCP.new(mock_file_system)
|
30
|
+
end
|
31
|
+
|
6
32
|
if @scp && (@scp.session.transport.port.to_s != self["ssh.port"] || (@scp.session.transport.options[:user] != self["login.username"]))
|
7
33
|
@scp = nil
|
8
34
|
end
|
@@ -17,11 +17,12 @@ module Asteroid
|
|
17
17
|
if @ssh && (@ssh.transport.port.to_s != self["ssh.port"] || (@ssh.transport.options[:user] != self["login.username"]))
|
18
18
|
@ssh = nil
|
19
19
|
end
|
20
|
+
|
20
21
|
@ssh ||= Net::SSH.start(
|
21
22
|
ip_address,
|
22
23
|
self["login.username"],
|
23
24
|
port: self["ssh.port"].to_i,
|
24
|
-
:keys => [server.
|
25
|
+
:keys => [server.ssh_key.private.filename]
|
25
26
|
)
|
26
27
|
end
|
27
28
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'fileutils'
|
2
2
|
|
3
3
|
module Asteroid
|
4
4
|
|
@@ -26,7 +26,10 @@ module Asteroid
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def config_filename
|
29
|
-
File.
|
29
|
+
unless File.directory?(Asteroid::Config.secret_instance_dir)
|
30
|
+
FileUtils.mkdir Asteroid::Config.secret_instance_dir
|
31
|
+
end
|
32
|
+
File.join(Asteroid::Config.secret_instance_dir, "/instance_#{@id}.yml")
|
30
33
|
end
|
31
34
|
|
32
35
|
def load_config_data
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Asteroid
|
4
|
+
class KeyReference
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def search_paths
|
8
|
+
@search_paths ||= []
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name)
|
13
|
+
@name = name
|
14
|
+
|
15
|
+
@public = nil
|
16
|
+
@private = nil
|
17
|
+
|
18
|
+
self.class.search_paths.each do |path|
|
19
|
+
if @public.nil?
|
20
|
+
key = File.join(path, "#{@name}.pub")
|
21
|
+
if File.exists? key
|
22
|
+
@public = key
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if @private.nil?
|
27
|
+
key = File.join(path, "#{@name}")
|
28
|
+
if File.exists? key
|
29
|
+
@private = key
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def public
|
36
|
+
@public && FileReference.new(@public)
|
37
|
+
end
|
38
|
+
|
39
|
+
def private
|
40
|
+
@private && FileReference.new(@private)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -21,17 +21,32 @@ module Asteroid
|
|
21
21
|
@config = config
|
22
22
|
end
|
23
23
|
|
24
|
+
def type
|
25
|
+
self.class.type
|
26
|
+
end
|
27
|
+
|
24
28
|
def self.type
|
25
29
|
self.to_s.split('::').last.underscore.to_sym
|
26
30
|
end
|
27
31
|
|
28
32
|
def instances
|
29
|
-
|
33
|
+
[]
|
30
34
|
end
|
31
35
|
|
32
36
|
def destroy_instance(instance)
|
33
37
|
end
|
34
38
|
|
39
|
+
protected
|
40
|
+
|
41
|
+
def require_attribute(o, att, message)
|
42
|
+
if o[att].nil?
|
43
|
+
raise [
|
44
|
+
"Can't create instance on #{instance_name} because #{message}:",
|
45
|
+
"Add Add #{att} to your server config .yml\n #{o.inspect}"
|
46
|
+
].join(' ')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
35
50
|
end
|
36
51
|
end
|
37
52
|
end
|
@@ -26,17 +26,36 @@ module Asteroid
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def create_instance(server)
|
29
|
+
|
30
|
+
instance_options = server.instance_options
|
31
|
+
|
32
|
+
require_attribute instance_options,
|
33
|
+
:size_id,
|
34
|
+
"needs size of droplet"
|
35
|
+
|
36
|
+
require_attribute instance_options,
|
37
|
+
:image_id,
|
38
|
+
"needs starting image for droplet"
|
39
|
+
|
40
|
+
require_attribute instance_options,
|
41
|
+
:region_id,
|
42
|
+
"needs region for droplet"
|
43
|
+
|
44
|
+
require_attribute instance_options,
|
45
|
+
:ssh_key_ids,
|
46
|
+
"needs SSH key registered for droplet"
|
47
|
+
|
29
48
|
instance = Digitalocean::Droplet.create(
|
30
|
-
name: server.
|
31
|
-
size_id:
|
32
|
-
image_id:
|
33
|
-
region_id:
|
34
|
-
ssh_key_ids:
|
49
|
+
name: server.generate_instance_name,
|
50
|
+
size_id: instance_options[:size_id],
|
51
|
+
image_id: instance_options[:image_id],
|
52
|
+
region_id: instance_options[:region_id],
|
53
|
+
ssh_key_ids: instance_options[:ssh_key_ids],
|
35
54
|
private_networking: true
|
36
55
|
)
|
37
56
|
|
38
57
|
if instance.status == "OK"
|
39
|
-
Instance.new instance.droplet.merge(provider: self)
|
58
|
+
Instance.new instance.droplet.marshal_dump.merge(provider: self)
|
40
59
|
else
|
41
60
|
nil
|
42
61
|
end
|
@@ -61,6 +80,16 @@ module Asteroid
|
|
61
80
|
def reboot_instance(instance)
|
62
81
|
end
|
63
82
|
|
83
|
+
private
|
84
|
+
|
85
|
+
def provider_name
|
86
|
+
"DigitalOcean"
|
87
|
+
end
|
88
|
+
|
89
|
+
def instance_name
|
90
|
+
"Droplet"
|
91
|
+
end
|
92
|
+
|
64
93
|
end
|
65
94
|
end
|
66
95
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
1
3
|
module Asteroid
|
2
4
|
module Provider
|
3
5
|
class Mock < Abstract
|
@@ -6,20 +8,23 @@ module Asteroid
|
|
6
8
|
(0..3).to_a.map{|a| rand(255)}.join(".")
|
7
9
|
end
|
8
10
|
|
11
|
+
def self.clear!
|
12
|
+
FileUtils.rm filename if File.exists? filename
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.filename
|
16
|
+
File.join(File.dirname(__FILE__), '../../../world.yml')
|
17
|
+
end
|
18
|
+
|
9
19
|
def initialize(config = {})
|
10
|
-
@config = config
|
11
|
-
@config[:filename] ||=
|
12
|
-
|
13
|
-
|
14
|
-
@world = YAML::load_file @config[:filename]
|
15
|
-
rescue
|
16
|
-
puts "No such file #{@config[:filename]}"
|
17
|
-
@world = {}
|
18
|
-
end
|
20
|
+
@config = config
|
21
|
+
@config[:filename] ||= self.class.filename
|
22
|
+
|
23
|
+
load_world
|
19
24
|
end
|
20
25
|
|
21
26
|
def instances
|
22
|
-
@
|
27
|
+
@world[:instances] ||= []
|
23
28
|
end
|
24
29
|
|
25
30
|
def create_instance(server)
|
@@ -27,23 +32,40 @@ module Asteroid
|
|
27
32
|
name: server.generate_instance_name,
|
28
33
|
id: SecureRandom.hex(5),
|
29
34
|
ip_address: self.class.ip_address,
|
35
|
+
type: server.type
|
36
|
+
}
|
37
|
+
|
38
|
+
self.instances << instance
|
39
|
+
save_world
|
40
|
+
|
41
|
+
Instance.new instance.merge({
|
30
42
|
provider: self,
|
31
|
-
type: server.type,
|
32
43
|
server: server
|
33
|
-
}
|
34
|
-
Instance.new instance
|
44
|
+
})
|
35
45
|
end
|
36
46
|
|
37
47
|
def destroy_instance(instance)
|
38
|
-
|
48
|
+
self.instances.delete_if do |i|
|
49
|
+
i[:id] == i.id
|
50
|
+
end
|
51
|
+
save_world
|
39
52
|
end
|
40
53
|
|
41
54
|
private
|
42
55
|
|
56
|
+
def load_world
|
57
|
+
begin
|
58
|
+
@world = YAML::load_file @config[:filename]
|
59
|
+
rescue
|
60
|
+
@world = {}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
43
64
|
def save_world
|
44
65
|
File.open(@config[:filename], 'w') do |f|
|
45
66
|
f.write @world.to_yaml
|
46
67
|
end
|
68
|
+
load_world
|
47
69
|
end
|
48
70
|
|
49
71
|
end
|
data/lib/asteroid/script.rb
CHANGED
@@ -1,64 +1,10 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
1
|
module Asteroid
|
4
|
-
class Script
|
5
|
-
|
6
|
-
def initialize(filename)
|
7
|
-
@filename = filename
|
8
|
-
end
|
9
|
-
|
10
|
-
def set_data(data)
|
11
|
-
@data = data
|
12
|
-
end
|
13
|
-
|
14
|
-
def data
|
15
|
-
@data ||= {}
|
16
|
-
end
|
17
|
-
|
18
|
-
def name
|
19
|
-
File.basename @filename
|
20
|
-
end
|
21
|
-
|
22
|
-
def file_type
|
23
|
-
@file_type ||= @filename.split('.').last
|
24
|
-
end
|
25
2
|
|
26
|
-
|
27
|
-
file_type == "yml"
|
28
|
-
end
|
29
|
-
|
30
|
-
def aster?
|
31
|
-
file_type == "aster"
|
32
|
-
end
|
3
|
+
class ScriptReference < FileReference
|
33
4
|
|
34
|
-
def
|
35
|
-
|
5
|
+
def executable?
|
6
|
+
[:yml, :aster].include?(type)
|
36
7
|
end
|
37
8
|
|
38
|
-
def render(locals = {})
|
39
|
-
script = File.read @filename
|
40
|
-
if template?
|
41
|
-
Template.new(:erb).render(
|
42
|
-
script,
|
43
|
-
self.data.merge(locals)
|
44
|
-
)
|
45
|
-
else
|
46
|
-
script
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.named(name)
|
51
|
-
all.select{|s| s.name == name}.first
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.all
|
55
|
-
files = Dir[Asteroid::Config.script_dir + "/*"]
|
56
|
-
files.map do |filename|
|
57
|
-
new(filename)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
9
|
end
|
64
10
|
end
|