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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/Gemfile +1 -0
  4. data/README.md +45 -4
  5. data/Rakefile +1 -0
  6. data/asteroid.gemspec +0 -1
  7. data/bin/asteroid +4 -2
  8. data/lib/asteroid.rb +11 -10
  9. data/lib/asteroid/application.rb +60 -7
  10. data/lib/asteroid/config.rb +28 -14
  11. data/lib/asteroid/file_reference.rb +102 -0
  12. data/lib/asteroid/generator.rb +0 -4
  13. data/lib/asteroid/instance.rb +29 -9
  14. data/lib/asteroid/instance/command.rb +71 -36
  15. data/lib/asteroid/instance/scp.rb +26 -0
  16. data/lib/asteroid/instance/ssh.rb +2 -1
  17. data/lib/asteroid/instance/vars.rb +5 -2
  18. data/lib/asteroid/key_reference.rb +45 -0
  19. data/lib/asteroid/provider/abstract.rb +16 -1
  20. data/lib/asteroid/provider/digital_ocean.rb +35 -6
  21. data/lib/asteroid/provider/mock.rb +36 -14
  22. data/lib/asteroid/script.rb +3 -57
  23. data/lib/asteroid/server.rb +44 -25
  24. data/lib/asteroid/template.rb +19 -7
  25. data/lib/asteroid/template/liquid.rb +0 -0
  26. data/lib/asteroid/version.rb +1 -1
  27. data/sample/.gitignore +2 -0
  28. data/sample/README.md +1 -0
  29. data/sample/Rakefile +0 -0
  30. data/sample/TODO +9 -0
  31. data/sample/asteroid/files/nginx/available_site.conf.erb +0 -0
  32. data/sample/asteroid/files/nginx/nginx.conf +1 -0
  33. data/sample/asteroid/scripts/ubuntu.aster +4 -0
  34. data/sample/asteroid/servers/default.yml +3 -0
  35. data/sample/asteroid/servers/web.yml +5 -0
  36. data/sample/config/asteroid.rb +9 -0
  37. data/test/helper.rb +18 -2
  38. data/test/unit/test_application.rb +17 -0
  39. data/test/unit/test_file_reference.rb +18 -0
  40. data/test/unit/test_instance.rb +20 -4
  41. data/test/unit/test_script_reference.rb +9 -0
  42. data/test/unit/test_server.rb +46 -2
  43. data/test/unit/test_template.rb +16 -0
  44. metadata +24 -17
  45. data/lib/asteroid/config_file.rb +0 -46
@@ -2,33 +2,39 @@
2
2
  module Asteroid
3
3
  class Instance
4
4
 
5
- def eval_command(cmd, env = nil)
6
- env ||= template_data
7
- cmd = Template.new(:erb).render(cmd, env)
8
- cmd, *rest = cmd.split(" ")
9
- case cmd.to_sym
10
- when :run
11
- self.ssh # reconnect
12
- command_run(rest.first, env)
13
- when :upload
14
- from, to, _ = rest
15
- command_upload(from, to, env)
16
- when :"upload-private-key"
17
- name, _ = rest
18
- command_upload_private_key(name, env)
19
- when :exec
20
- script = rest.join(" ")
21
- command_exec(script, env)
22
- when :config
23
- set_or_get, var, val, _ = rest
24
- command_config(set_or_get, var, val)
25
- else
26
- if server.commands[cmd]
27
- command_command(cmd, rest, env)
28
- else
29
- binding.pry
30
- end
31
- end
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(script, env)
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
- e.define_function :run, [] do |arguments|
78
- binding.pry
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
- e.define_function :exec, [] do |arguments|
82
- binding.pry
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 = ConfigFile.new(from)
157
+ from = FileReference.new(from)
124
158
 
125
159
  if from.template?
126
- from.set_data env
160
+ from.data = env
127
161
  end
128
162
 
129
- puts from.filename
130
- self.scp.upload! from.filename, to
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.ssh_key_filename]
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.join(Asteroid::Config.instance_config_dir, "/instance_#{@id}.yml")
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.instance_name,
31
- size_id: server.attributes[:size_id],
32
- image_id: server.attributes[:image_id],
33
- region_id: server.attributes[:region_id],
34
- ssh_key_ids: server.attributes[: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] ||= File.join(File.dirname(__FILE__), '../../../world.yml')
12
-
13
- begin
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
- @config[:instances] || []
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
@@ -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
- def yml?
27
- file_type == "yml"
28
- end
29
-
30
- def aster?
31
- file_type == "aster"
32
- end
3
+ class ScriptReference < FileReference
33
4
 
34
- def template?
35
- @filename.split('.').last == "erb"
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