nova 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +19 -0
  3. data/README.md +29 -0
  4. data/bin/nova +8 -0
  5. data/lib/generator/template/new_install/galaxy/some_star.rb +3 -0
  6. data/lib/generator/template/new_install/supernova.yml +16 -0
  7. data/lib/nova.rb +49 -0
  8. data/lib/nova/cli.rb +62 -0
  9. data/lib/nova/commands/server.rb +71 -0
  10. data/lib/nova/common.rb +17 -0
  11. data/lib/nova/common/event_handler.rb +165 -0
  12. data/lib/nova/common/event_handler/event.rb +147 -0
  13. data/lib/nova/common/features.rb +93 -0
  14. data/lib/nova/common/features/feature.rb +65 -0
  15. data/lib/nova/common/metadata.rb +65 -0
  16. data/lib/nova/common/metadata/data.rb +171 -0
  17. data/lib/nova/common/star_management.rb +164 -0
  18. data/lib/nova/constructor.rb +84 -0
  19. data/lib/nova/exceptions.rb +16 -0
  20. data/lib/nova/project.rb +199 -0
  21. data/lib/nova/remote.rb +10 -0
  22. data/lib/nova/remote/fake.rb +51 -0
  23. data/lib/nova/remote/fake/commands.rb +44 -0
  24. data/lib/nova/remote/fake/file_system.rb +76 -0
  25. data/lib/nova/remote/fake/operating_system.rb +52 -0
  26. data/lib/nova/remote/fake/platform.rb +89 -0
  27. data/lib/nova/star.rb +25 -0
  28. data/lib/nova/starbound.rb +14 -0
  29. data/lib/nova/starbound/client.rb +59 -0
  30. data/lib/nova/starbound/encryptor.rb +134 -0
  31. data/lib/nova/starbound/encryptors.rb +13 -0
  32. data/lib/nova/starbound/encryptors/openssl.rb +122 -0
  33. data/lib/nova/starbound/encryptors/plaintext.rb +64 -0
  34. data/lib/nova/starbound/encryptors/rbnacl.rb +67 -0
  35. data/lib/nova/starbound/protocol.rb +81 -0
  36. data/lib/nova/starbound/protocol/encryption.rb +48 -0
  37. data/lib/nova/starbound/protocol/exceptions.rb +38 -0
  38. data/lib/nova/starbound/protocol/messages.rb +116 -0
  39. data/lib/nova/starbound/protocol/packet.rb +267 -0
  40. data/lib/nova/starbound/protocol/socket.rb +231 -0
  41. data/lib/nova/starbound/server.rb +182 -0
  42. data/lib/nova/version.rb +5 -0
  43. data/spec/constructor_spec.rb +20 -0
  44. data/spec/local_spec.rb +26 -0
  45. data/spec/nova_spec.rb +7 -0
  46. data/spec/spec_helper.rb +19 -0
  47. data/spec/star/some_type.rb +27 -0
  48. data/spec/star_spec.rb +107 -0
  49. data/spec/starbound/encryptor_spec.rb +33 -0
  50. data/spec/starbound/openssl_encryptor_spec.rb +80 -0
  51. data/spec/starbound/packet_spec.rb +61 -0
  52. data/spec/starbound/plaintext_encryptor_spec.rb +27 -0
  53. data/spec/starbound/protocol_spec.rb +163 -0
  54. data/spec/starbound/rbnacl_encryptor_spec.rb +70 -0
  55. metadata +166 -0
@@ -0,0 +1,164 @@
1
+ module Nova
2
+ module Common
3
+
4
+ # Manages types of stars. Adds a +star_type+ method to subclasses
5
+ # to add it to the type list.
6
+ module StarManagement
7
+
8
+ # Class methods.
9
+ module ClassMethods
10
+
11
+ # All of the types of stars. Should be a key-value pair, with
12
+ # the key being the name, and the value being the class.
13
+ #
14
+ # @return [Hash{Symbol => Class}]
15
+ def types
16
+ @@types ||= {}
17
+ end
18
+
19
+ # When the star is subclassed, add the subclass
20
+ # automatically to the star type list, unless it doesn't
21
+ # have a proper name.
22
+ #
23
+ # @api private
24
+ def inherited(klass)
25
+ return unless klass.name
26
+
27
+ type = klass.name.gsub(/([A-Z])/) { |a| "_#{a.downcase}" }.gsub("::", "/")[1..-1].intern
28
+ klass.star_type(type)
29
+ end
30
+
31
+ # Adds the Star to the type list.
32
+ #
33
+ # @param name [Symbol] the name of the star.
34
+ # @return [self]
35
+ def star_type(name)
36
+ types.delete_if { |_, v| v == self }
37
+ types[name] = self
38
+ self.type = name
39
+ stars[name] = {}
40
+ self
41
+ end
42
+
43
+ # All of the stars that have been defined. These are
44
+ # different from star types because they contain information
45
+ # such as events.
46
+ #
47
+ # @return [Hash{Symbol => Class}]
48
+ def stars
49
+ @@stars ||= {}
50
+ end
51
+
52
+ # An accessor for {#stars}.
53
+ #
54
+ # @param star_name [Symbol]
55
+ # @return [Hash, Class]
56
+ def [](star_name)
57
+ stars[star_name]
58
+ end
59
+
60
+ # Cleans up the inspect a little bit.
61
+ #
62
+ # @return [String]
63
+ def inspect
64
+ @_inspect ||=
65
+ ancestors.take_while { |x| x <= Star }.map(&:name).reverse.join("/").gsub(/\/\z/, "." + as.to_s)
66
+ end
67
+
68
+ # Just a way to write it; syntaxic sugar. It returns what
69
+ # was passed.
70
+ #
71
+ # @example
72
+ # Nova::Star/Type.something
73
+ # @param other_class [Class]
74
+ # @return [Class] other_class.
75
+ def /(other_class)
76
+ other_class
77
+ end
78
+
79
+ # The remote to use, by default, for stars.
80
+ #
81
+ # @!parse attr_reader :remote
82
+ # @return [Module]
83
+ def remote
84
+ @remote ||= Remote::Fake
85
+ end
86
+
87
+ attr_writer :remote
88
+
89
+ # The name of the star.
90
+ #
91
+ # @return [Symbol]
92
+ attr_accessor :as
93
+
94
+ # The type of the star.
95
+ #
96
+ # @return [Symbol]
97
+ attr_accessor :type
98
+
99
+ # Retrieves the star with the given name.
100
+ #
101
+ # @example
102
+ # Nova::Star/Type.klass
103
+ # @return [Class]
104
+ def method_missing(method, *args, &block)
105
+ if (stars.key?(method) || stars[type].key?(method)) && args.length == 0
106
+ stars[method] || stars[type][method]
107
+ else
108
+ super
109
+ end
110
+ end
111
+ end
112
+
113
+ # Instance methods.
114
+ module InstanceMethods
115
+
116
+ # Checks for the correct platforms in initialization. If
117
+ # it's not on the right platform, raises an error.
118
+ #
119
+ # @raise [NoPlatformError] if it's not available on the
120
+ # platform.
121
+ def initialize(remote = nil)
122
+ @remote = (remote || self.remote).new
123
+ super(@remote)
124
+ end
125
+
126
+ # Cleans up the inspect a little bit.
127
+ #
128
+ # @return [String]
129
+ def inspect
130
+ @_inspect ||= begin
131
+ "#<" <<
132
+ self.class.inspect <<
133
+ (":0x%014x" % object_id) <<
134
+ ">"
135
+ end
136
+ end
137
+
138
+ # The remote this star is using. Can be set locally, but
139
+ # uses the global remote by default.
140
+ #
141
+ # @see ClassMethods#remote
142
+ # @!parse attr_reader :remote
143
+ # @return [Module]
144
+ def remote
145
+ @remote || self.class.remote
146
+ end
147
+
148
+ attr_writer :remote
149
+
150
+ end
151
+
152
+ # Called when {StarManagement} is included. Extends what included
153
+ # it by {ClassMethods}, and includes {InstanceMethods}.
154
+ #
155
+ # @param receiver [Object]
156
+ # @return [void]
157
+ # @api private
158
+ def self.included(receiver)
159
+ receiver.send :include, InstanceMethods
160
+ receiver.extend ClassMethods
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,84 @@
1
+ module Nova
2
+
3
+ # Creates a star from a {Nova.create} call.
4
+ class Constructor
5
+
6
+ # Initialize the constructor.
7
+ #
8
+ # @param options [Hash] the definition of the star. It should
9
+ # be a single key-value pair, with the first key being the
10
+ # type of star, and the first value being the name of the
11
+ # star.
12
+ # @yield for the construction of the new Star.
13
+ # @example
14
+ # Constructor.new(some_star: :another_star) do
15
+ # on :some_event do; end
16
+ # end.create # => Nova::Star/SomeStar.another_star
17
+ def initialize(options, &block)
18
+ @options = options
19
+ @block = block
20
+ end
21
+
22
+ # Modifies an already existing star if it exists, or creates it
23
+ # if it doesn't.
24
+ #
25
+ # @raise [NoStarError] when the star type couldn't be found.
26
+ # @return [Class] a subclass of the star type.
27
+ def modify_or_create
28
+ star_type = Star.types[data[:type]]
29
+
30
+ raise NoStarError,
31
+ "Could not find star type #{data[:type]}." unless star_type
32
+
33
+ if Star.stars[data[:type]][data[:as]]
34
+ handle_existing
35
+ else
36
+ handle_new star_type
37
+ end
38
+ end
39
+
40
+
41
+ # Returns information about the star, like the type, the required
42
+ # platforms, and what it's named.
43
+ #
44
+ # @return [Hash<Symbol, Object>]
45
+ def data
46
+ @_data ||= {
47
+ :as => @options.values.first,
48
+ :type => @options.keys.first
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ # Handles an existing star. Executes the block in the instance of
55
+ # the star, adds the definition's required_platforms to the stars,
56
+ # and then returns the star.
57
+ #
58
+ # @return [Class]
59
+ def handle_existing
60
+ star = Star.stars[data[:type]][data[:as]]
61
+
62
+ star.class_exec &@block
63
+
64
+ star
65
+ end
66
+
67
+ # Handles defining a new star. Creates a class as a subclass of
68
+ # the star, sets its name and type, and executes the block in the
69
+ # instance of the star. Adds the required_platform to the star,
70
+ # sets the star to {Star.stars}, and returns the new star.
71
+ #
72
+ # @param star_type [Class] the type of star it is.
73
+ # @return [Class]
74
+ def handle_new(star_type)
75
+ new_star = Class.new(star_type)
76
+ new_star.as = data[:as]
77
+ new_star.type = data[:type]
78
+ new_star.class_exec &@block
79
+
80
+ Star.stars[data[:type]][data[:as]] = new_star
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,16 @@
1
+ module Nova
2
+
3
+ # Raised when an Event cannot be found.
4
+ class NoEventError < StandardError; end
5
+
6
+ # Raised when options were passed that were not valid.
7
+ class InvalidOptionsError < StandardError; end
8
+
9
+ # Raised when a Star is tried to be created from a non-existant
10
+ # star type.
11
+ class NoStarError < StandardError; end
12
+
13
+ # Raised when a Star is tried to be instantized on a non-compliant
14
+ # platform.
15
+ class NoPlatformError < StandardError; end
16
+ end
@@ -0,0 +1,199 @@
1
+ require 'yaml'
2
+
3
+ module Nova
4
+
5
+ # A Nova project, containing the galaxy and configuration
6
+ # settings for that project.
7
+ class Project
8
+
9
+ # The default paths to load from.
10
+ DEFAULT_PATHS = [File.absolute_path("../../../galaxy", __FILE__)]
11
+
12
+ # Whether or not the given directory is deemable as a Nova
13
+ # project.
14
+ #
15
+ # @param dir [String] the directory to test.
16
+ # @return [Boolean]
17
+ def self.valid?(dir)
18
+ Dir.new(dir).each.include?("nova.yml")
19
+ end
20
+
21
+ # The directory the project is based in.
22
+ #
23
+ # @return [Directory]
24
+ attr_reader :directory
25
+
26
+ # The load paths for this project.
27
+ #
28
+ # @return [Array<String>]
29
+ attr_reader :load_paths
30
+
31
+ # The options that were loaded from the config for this project.
32
+ #
33
+ # @return [Hash]
34
+ attr_reader :options
35
+
36
+ # Initializes the project. Loads the configuration file by
37
+ # default.
38
+ #
39
+ # @param dir [String] the path to the directory for the project.
40
+ # @param load_config [Boolean] whether or not to load the
41
+ # configuration file.
42
+ def initialize(dir, load_config = true)
43
+ @directory = Dir.new(dir)
44
+ @load_paths = DEFAULT_PATHS.dup
45
+ @options = {}
46
+
47
+ if load_config
48
+ load_config!
49
+ end
50
+ end
51
+
52
+
53
+ # Loads the configuration file.
54
+ #
55
+ # @return [Hash] the data.
56
+ def load_config!
57
+ return unless options.empty?
58
+
59
+ data = ::YAML.load_file(File.open("#{directory.path}/nova.yml", "r"))
60
+ load_paths.push(*data.fetch("load_paths", []))
61
+
62
+ load_paths.map! do |path|
63
+ File.absolute_path(path, directory.path)
64
+ end
65
+
66
+ @options = data
67
+ end
68
+
69
+ # Requires all of the star files that is in the project.
70
+ #
71
+ # @return [void]
72
+ def require_files
73
+ @load_paths.each do |path|
74
+ Dir["#{path}/**/*"].each do |f|
75
+ require f
76
+ end
77
+ end
78
+ end
79
+
80
+ # Runs the servers defined in the options.
81
+ #
82
+ # @note If do_fork is false, only the first server in the config
83
+ # file will actually be created.
84
+ # @param do_fork [Boolean] whether or not to actually fork the
85
+ # process when creating servers.
86
+ # @param which [Array<String>] which servers to run. Defaults to
87
+ # all of them.
88
+ # @return [void]
89
+ def run_servers(do_fork = true, which = [])
90
+ each_server(which) do |server, name|
91
+ puts name
92
+
93
+ if File.exists?(server[:files][:pid])
94
+ Nova.logger.warn {
95
+ "PID file #{server[:files][:pid]} already exists. " +
96
+ "Ignoring server definition."
97
+ }
98
+ next
99
+ end
100
+
101
+ if do_fork
102
+ process_id = fork
103
+ end
104
+
105
+ if process_id
106
+ File.open(server[:files][:pid], "w") { |f| f.write process_id }
107
+ Process.detach(process_id)
108
+ else
109
+ return build_server(server, do_fork)
110
+ end
111
+ end
112
+ end
113
+
114
+ # Takes down running servers.
115
+ #
116
+ # @param which [Array<String>] which servers to take down.
117
+ # Defaults to all of them.
118
+ # @return [void]
119
+ def shoot(which = [])
120
+ each_server do |server, name|
121
+ if File.exists?(server[:files][:pid])
122
+ pid = File.open(server[:files][:pid], "r") { |f| f.read }.to_i
123
+
124
+ puts "Sending INT to #{pid}..."
125
+
126
+ Process.kill :INT, pid rescue Errno::ESRCH
127
+
128
+ File.delete(server[:files][:pid]) rescue Errno::ENOENT
129
+
130
+ puts "OK!"
131
+ end
132
+ end
133
+ end
134
+
135
+ private
136
+
137
+ # Loops over all of the defined servers, yielding the server
138
+ # definition and the index for that server.
139
+ #
140
+ # @yieldparam server [Hash<Symbol, Object>] the server data
141
+ # @yieldparam server_name [String] the name of the server.
142
+ # @yieldparam index [Numeric] the index of the server in the
143
+ # definition file.
144
+ # @return [void]
145
+ def each_server(only = [])
146
+ server_list = [options["servers"], options["server"]].flatten.compact
147
+
148
+ server_list.each_with_index do |srv, i|
149
+ srv_name = srv.fetch(:name, "server#{i}")
150
+ srv[:files] ||= {}
151
+
152
+ files = {}
153
+ {:log => :log, :pid => :pid, :client => :rb}.each do |f, n|
154
+ files[f] = File.absolute_path(
155
+ srv[:files].fetch(f, "./#{srv_name}.#{n}"),
156
+ directory.path
157
+ )
158
+ end
159
+ srv[:files] = files
160
+
161
+ next unless only.empty? or only.include?(srv_name)
162
+
163
+ yield srv, srv_name, i
164
+ end
165
+ end
166
+
167
+ # Creates a server, with the given server options.
168
+ #
169
+ # @param server [Hash<Symbol, Object>] the server information to
170
+ # base the server instance off of.
171
+ # @param redirect [Boolean] whether or not to redirect the
172
+ # {Nova.logger} and the stdin, stdout, and stderr for the
173
+ # server.
174
+ # @return [void]
175
+ def build_server(server, redirect = true)
176
+ if redirect
177
+ Nova.logger = Logger.new(server[:files][:log], 10, 1_024_000)
178
+ $stdin = $stdout = $stderr = File.open("/dev/null", "a")
179
+ end
180
+
181
+ begin
182
+ s = Nova::Starbound::Server.new(server)
183
+ s.read_client_file server[:files][:client]
184
+
185
+ trap :INT do
186
+ s.shutdown
187
+ File.delete(server[:files][:pid]) rescue Errno::ENOENT
188
+ end
189
+
190
+ return s.listen
191
+ rescue => e
192
+ Nova.logger.fatal { "#{e}: #{e.message}; #{e.backtrace[0]}" }
193
+ File.delete(server[:files][:pid]) rescue Errno::ENOENT
194
+ exit
195
+ end
196
+ end
197
+
198
+ end
199
+ end