rflow 0.0.5 → 1.0.0a1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +21 -0
- data/.yardopts +1 -0
- data/Gemfile +5 -1
- data/Guardfile +8 -0
- data/LICENSE +190 -0
- data/NOTES +26 -13
- data/README.md +448 -0
- data/Rakefile +5 -12
- data/bin/rflow +23 -20
- data/example/basic_config.rb +2 -2
- data/example/basic_extensions.rb +8 -8
- data/example/http_config.rb +1 -1
- data/example/http_extensions.rb +15 -15
- data/lib/rflow.rb +15 -387
- data/lib/rflow/component.rb +105 -50
- data/lib/rflow/component/port.rb +25 -24
- data/lib/rflow/components/raw.rb +4 -4
- data/lib/rflow/components/raw/extensions.rb +2 -2
- data/lib/rflow/configuration.rb +54 -36
- data/lib/rflow/configuration/component.rb +2 -3
- data/lib/rflow/configuration/connection.rb +9 -10
- data/lib/rflow/configuration/migrations/{20010101000001_create_settings.rb → 20010101000000_create_settings.rb} +2 -2
- data/lib/rflow/configuration/migrations/20010101000001_create_shards.rb +21 -0
- data/lib/rflow/configuration/migrations/20010101000002_create_components.rb +7 -2
- data/lib/rflow/configuration/migrations/20010101000003_create_ports.rb +3 -3
- data/lib/rflow/configuration/migrations/20010101000004_create_connections.rb +2 -2
- data/lib/rflow/configuration/port.rb +3 -4
- data/lib/rflow/configuration/ruby_dsl.rb +59 -35
- data/lib/rflow/configuration/setting.rb +8 -7
- data/lib/rflow/configuration/shard.rb +24 -0
- data/lib/rflow/configuration/uuid_keyed.rb +3 -3
- data/lib/rflow/connection.rb +21 -10
- data/lib/rflow/connections/zmq_connection.rb +45 -44
- data/lib/rflow/logger.rb +67 -0
- data/lib/rflow/master.rb +127 -0
- data/lib/rflow/message.rb +14 -14
- data/lib/rflow/pid_file.rb +84 -0
- data/lib/rflow/shard.rb +148 -0
- data/lib/rflow/version.rb +1 -1
- data/rflow.gemspec +22 -28
- data/schema/message.avsc +8 -8
- data/spec/fixtures/config_ints.rb +4 -4
- data/spec/fixtures/config_shards.rb +30 -0
- data/spec/fixtures/extensions_ints.rb +8 -8
- data/spec/rflow_component_port_spec.rb +58 -0
- data/spec/rflow_configuration_ruby_dsl_spec.rb +148 -0
- data/spec/rflow_configuration_spec.rb +4 -4
- data/spec/rflow_message_data_raw.rb +2 -2
- data/spec/rflow_message_data_spec.rb +6 -6
- data/spec/rflow_message_spec.rb +13 -13
- data/spec/rflow_spec.rb +294 -71
- data/spec/schema_spec.rb +2 -2
- data/spec/spec_helper.rb +6 -4
- data/temp.rb +21 -21
- metadata +56 -65
- data/.rvmrc +0 -1
- data/README +0 -0
@@ -6,12 +6,13 @@ class RFlow
|
|
6
6
|
class Component < ConfigDB
|
7
7
|
include UUIDKeyed
|
8
8
|
include ActiveModel::Validations
|
9
|
-
|
9
|
+
|
10
10
|
class ComponentInvalid < StandardError; end
|
11
11
|
class ComponentNotFound < StandardError; end
|
12
12
|
|
13
13
|
serialize :options, Hash
|
14
14
|
|
15
|
+
belongs_to :shard, :primary_key => 'uuid', :foreign_key => 'shard_uuid'
|
15
16
|
has_many :input_ports, :primary_key => 'uuid', :foreign_key => 'component_uuid'
|
16
17
|
has_many :output_ports, :primary_key => 'uuid', :foreign_key => 'component_uuid'
|
17
18
|
|
@@ -19,9 +20,7 @@ class RFlow
|
|
19
20
|
#has_many :input_connections, :through => :input_ports, :source => :input_connections
|
20
21
|
#has_many :output_connections, :through => :output_ports, :source => :output_connection
|
21
22
|
|
22
|
-
|
23
23
|
validates_uniqueness_of :name
|
24
|
-
|
25
24
|
end
|
26
25
|
end
|
27
26
|
end
|
@@ -10,18 +10,18 @@ class RFlow
|
|
10
10
|
include ActiveModel::Validations
|
11
11
|
|
12
12
|
serialize :options, Hash
|
13
|
-
|
13
|
+
|
14
14
|
belongs_to :input_port, :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
|
15
15
|
belongs_to :output_port,:primary_key => 'uuid', :foreign_key => 'output_port_uuid'
|
16
16
|
|
17
17
|
before_create :merge_default_options!
|
18
|
-
|
18
|
+
|
19
19
|
validates_uniqueness_of :uuid
|
20
20
|
validates_presence_of :output_port_uuid, :input_port_uuid
|
21
21
|
|
22
22
|
validate :all_required_options_present
|
23
23
|
|
24
|
-
|
24
|
+
|
25
25
|
def all_required_options_present
|
26
26
|
self.class.required_options.each do |option_name|
|
27
27
|
unless self.options.include? option_name.to_s
|
@@ -30,7 +30,7 @@ class RFlow
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
|
34
34
|
def merge_default_options!
|
35
35
|
self.options ||= {}
|
36
36
|
self.class.default_options.each do |option_name, default_value_or_proc|
|
@@ -43,7 +43,7 @@ class RFlow
|
|
43
43
|
# used in validations. To be overridden.
|
44
44
|
def self.required_options; []; end
|
45
45
|
|
46
|
-
|
46
|
+
|
47
47
|
# Should return a hash of default options, where the keys are
|
48
48
|
# the option names and the values are either default option
|
49
49
|
# values or Procs that take a single connection argument. This
|
@@ -53,7 +53,7 @@ class RFlow
|
|
53
53
|
|
54
54
|
end
|
55
55
|
|
56
|
-
|
56
|
+
|
57
57
|
# STI Subclass for ZMQ connections and their required options
|
58
58
|
class ZMQConnection < Connection
|
59
59
|
|
@@ -61,15 +61,15 @@ class RFlow
|
|
61
61
|
{
|
62
62
|
'output_socket_type' => 'PUSH',
|
63
63
|
'output_address' => lambda{|conn| "ipc://rflow.#{conn.uuid}"},
|
64
|
-
'output_responsibility' => '
|
64
|
+
'output_responsibility' => 'connect',
|
65
65
|
'input_socket_type' => 'PULL',
|
66
66
|
'input_address' => lambda{|conn| "ipc://rflow.#{conn.uuid}"},
|
67
|
-
'input_responsibility' => '
|
67
|
+
'input_responsibility' => 'bind',
|
68
68
|
}
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
-
|
72
|
+
|
73
73
|
# STI Subclass for AMQP connections and their required options
|
74
74
|
class AMQPConnection < Connection
|
75
75
|
|
@@ -95,4 +95,3 @@ class RFlow
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
98
|
-
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateShards < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table(:shards, :id => false) do |t|
|
4
|
+
t.string :uuid, :limit => 36, :primary => true
|
5
|
+
t.string :name
|
6
|
+
t.integer :count
|
7
|
+
|
8
|
+
# STI
|
9
|
+
t.string :type
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :shards, :uuid, :unique => true
|
15
|
+
add_index :shards, :name, :unique => true
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.down
|
19
|
+
drop_table :shards
|
20
|
+
end
|
21
|
+
end
|
@@ -6,13 +6,18 @@ class CreateComponents < ActiveRecord::Migration
|
|
6
6
|
t.boolean :managed, :default => true
|
7
7
|
t.text :specification
|
8
8
|
t.text :options
|
9
|
-
|
9
|
+
|
10
|
+
# UUID version of belongs_to :shard
|
11
|
+
t.string :shard_uuid
|
12
|
+
|
10
13
|
t.timestamps
|
11
14
|
end
|
12
15
|
|
13
16
|
add_index :components, :uuid, :unique => true
|
17
|
+
add_index :components, :name, :unique => true
|
18
|
+
add_index :components, :shard_uuid
|
14
19
|
end
|
15
|
-
|
20
|
+
|
16
21
|
def self.down
|
17
22
|
drop_table :components
|
18
23
|
end
|
@@ -7,9 +7,9 @@ class CreatePorts < ActiveRecord::Migration
|
|
7
7
|
# For STI
|
8
8
|
t.text :type
|
9
9
|
|
10
|
-
# UUID version of belongs_to :component
|
10
|
+
# UUID version of belongs_to :component
|
11
11
|
t.string :component_uuid
|
12
|
-
|
12
|
+
|
13
13
|
t.timestamps
|
14
14
|
end
|
15
15
|
|
@@ -17,7 +17,7 @@ class CreatePorts < ActiveRecord::Migration
|
|
17
17
|
add_index :ports, :component_uuid
|
18
18
|
add_index :ports, [:component_uuid, :name], :unique => true
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def self.down
|
22
22
|
drop_table :ports
|
23
23
|
end
|
@@ -14,13 +14,13 @@ class CreateConnections < ActiveRecord::Migration
|
|
14
14
|
t.string :input_port_key, :default => '0'
|
15
15
|
|
16
16
|
t.text :options
|
17
|
-
|
17
|
+
|
18
18
|
t.timestamps
|
19
19
|
end
|
20
20
|
|
21
21
|
add_index :connections, :uuid, :unique => true
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def self.down
|
25
25
|
drop_table :connections
|
26
26
|
end
|
@@ -6,7 +6,7 @@ class RFlow
|
|
6
6
|
class Port < ConfigDB
|
7
7
|
include UUIDKeyed
|
8
8
|
include ActiveModel::Validations
|
9
|
-
|
9
|
+
|
10
10
|
class PortInvalid < StandardError; end
|
11
11
|
|
12
12
|
belongs_to :component, :primary_key => 'uuid', :foreign_key => 'component_uuid'
|
@@ -18,13 +18,12 @@ class RFlow
|
|
18
18
|
# STI-based classes
|
19
19
|
class InputPort < Port;
|
20
20
|
has_many :input_connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
|
21
|
-
has_many :
|
21
|
+
has_many :connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'input_port_uuid'
|
22
22
|
end
|
23
23
|
|
24
24
|
class OutputPort < Port;
|
25
25
|
has_many :output_connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'output_port_uuid'
|
26
|
-
has_many :
|
26
|
+
has_many :connections, :class_name => 'RFlow::Configuration::Connection', :primary_key => 'uuid', :foreign_key => 'output_port_uuid'
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
@@ -6,12 +6,14 @@ class RFlow
|
|
6
6
|
# Ruby DSL config file controller.
|
7
7
|
# TODO: more docs and examples
|
8
8
|
class RubyDSL
|
9
|
-
attr_accessor :setting_specs, :
|
10
|
-
|
9
|
+
attr_accessor :setting_specs, :shard_specs, :connection_specs, :allocated_system_ports
|
10
|
+
|
11
11
|
def initialize
|
12
12
|
@setting_specs = []
|
13
|
-
@
|
13
|
+
@shard_specs = [{:name => "DEFAULT", :type => :process, :count => 1, :components => []}]
|
14
14
|
@connection_specs = []
|
15
|
+
|
16
|
+
@current_shard = @shard_specs.first
|
15
17
|
end
|
16
18
|
|
17
19
|
# Helper function to extract the line of the config that
|
@@ -19,7 +21,7 @@ class RFlow
|
|
19
21
|
def get_config_line(call_history)
|
20
22
|
call_history.first.split(':in').first
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
# DSL method to specify a name/value pair. RFlow core uses the
|
24
26
|
# 'rflow.' prefix on all of its settings. Custom settings
|
25
27
|
# should use a custom (unique) prefix
|
@@ -27,11 +29,28 @@ class RFlow
|
|
27
29
|
setting_specs << {:name => setting_name.to_s, :value => setting_value.to_s, :config_line => get_config_line(caller)}
|
28
30
|
end
|
29
31
|
|
32
|
+
# DSL method to specify a shard block for either a process or thread
|
33
|
+
def shard(shard_name, shard_options={})
|
34
|
+
raise ArgumentError, "Cannot use DEFAULT as a shard name" if shard_name == 'DEFAULT'
|
35
|
+
shard_type = if shard_options[:thread] || shard_options[:type] == :thread
|
36
|
+
:thread
|
37
|
+
else
|
38
|
+
:process
|
39
|
+
end
|
40
|
+
|
41
|
+
shard_count = shard_options[shard_type] || shard_options[:count] || 1
|
42
|
+
|
43
|
+
@current_shard = {:name => shard_name, :type => shard_type, :count => shard_count, :components => [], :config_line => get_config_line(caller)}
|
44
|
+
@shard_specs << @current_shard
|
45
|
+
yield self
|
46
|
+
@current_shard = @shard_specs.first
|
47
|
+
end
|
48
|
+
|
30
49
|
# DSL method to specify a component. Expects a name,
|
31
50
|
# specification, and set of component specific options, that
|
32
51
|
# must be marshallable into the database (i.e. should all be strings)
|
33
52
|
def component(component_name, component_specification, component_options={})
|
34
|
-
|
53
|
+
@current_shard[:components] << {
|
35
54
|
:name => component_name,
|
36
55
|
:specification => component_specification.to_s, :options => component_options,
|
37
56
|
:config_line => get_config_line(caller)
|
@@ -55,9 +74,9 @@ class RFlow
|
|
55
74
|
input_component_name, input_port_name, input_port_key = parse_connection_string(input_string)
|
56
75
|
|
57
76
|
connection_specs << {
|
58
|
-
:name => output_string + '=>' + input_string,
|
77
|
+
:name => output_string + '=>' + input_string,
|
59
78
|
:output_component_name => output_component_name,
|
60
|
-
:output_port_name => output_port_name, :output_port_key => output_port_key,
|
79
|
+
:output_port_name => output_port_name, :output_port_key => output_port_key,
|
61
80
|
:output_string => output_string,
|
62
81
|
:input_component_name => input_component_name,
|
63
82
|
:input_port_name => input_port_name, :input_port_key => input_port_key,
|
@@ -76,16 +95,16 @@ class RFlow
|
|
76
95
|
[matched[1], matched[2], (matched[3] || nil)]
|
77
96
|
end
|
78
97
|
|
79
|
-
|
98
|
+
|
80
99
|
# Method to process the 'DSL' objects into the config database
|
81
100
|
# via ActiveRecord
|
82
101
|
def process
|
83
102
|
process_setting_specs
|
84
|
-
|
103
|
+
process_shard_specs
|
85
104
|
process_connection_specs
|
86
105
|
end
|
87
106
|
|
88
|
-
|
107
|
+
|
89
108
|
# Iterates through each setting specified in the DSL and
|
90
109
|
# creates rows in the database corresponding to the setting
|
91
110
|
def process_setting_specs
|
@@ -95,17 +114,36 @@ class RFlow
|
|
95
114
|
end
|
96
115
|
end
|
97
116
|
|
98
|
-
|
99
|
-
# Iterates through each
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
RFlow
|
117
|
+
|
118
|
+
# Iterates through each shard specified in the DSL and creates
|
119
|
+
# rows in the database corresponding to the shard and included
|
120
|
+
# components
|
121
|
+
def process_shard_specs
|
122
|
+
@shard_specs.each do |shard_spec|
|
123
|
+
RFlow.logger.debug "Found #{shard_spec[:type]} shard '#{shard_spec[:name]}', creating"
|
124
|
+
|
125
|
+
shard_class = case shard_spec[:type]
|
126
|
+
when :process
|
127
|
+
RFlow::Configuration::ProcessShard
|
128
|
+
when :thread
|
129
|
+
RFlow::Configuration::ThreadShard
|
130
|
+
else
|
131
|
+
raise RFlow::Configuration::Shard::ShardInvalid, "Invalid shard: #{shard_spec.inspect}"
|
132
|
+
end
|
133
|
+
|
134
|
+
shard = shard_class.create! :name => shard_spec[:name], :count => shard_spec[:count]
|
135
|
+
|
136
|
+
shard_spec[:components].each do |component_spec|
|
137
|
+
RFlow.logger.debug "Shard '#{shard_spec[:name]}' found component '#{component_spec[:name]}', creating"
|
138
|
+
RFlow::Configuration::Component.create!(:shard => shard,
|
139
|
+
:name => component_spec[:name],
|
140
|
+
:specification => component_spec[:specification],
|
141
|
+
:options => component_spec[:options])
|
142
|
+
end
|
105
143
|
end
|
106
144
|
end
|
107
145
|
|
108
|
-
|
146
|
+
|
109
147
|
# Iterates through each component specified in the DSL and uses
|
110
148
|
# 'process_connection' to insert all the parts of the connection
|
111
149
|
# into the database
|
@@ -121,36 +159,22 @@ class RFlow
|
|
121
159
|
# ZeroMQ ipc sockets
|
122
160
|
def process_connection_spec(connection_spec)
|
123
161
|
RFlow.logger.debug "Found connection from '#{connection_spec[:output_string]}' to '#{connection_spec[:input_string]}', creating"
|
124
|
-
|
162
|
+
|
125
163
|
# an input port can be associated with multiple outputs, but
|
126
164
|
# an output port can only be associated with one input
|
127
165
|
output_component = RFlow::Configuration::Component.find_by_name connection_spec[:output_component_name]
|
128
166
|
raise RFlow::Configuration::Component::ComponentNotFound, "#{connection_spec[:output_component_name]}" unless output_component
|
129
167
|
output_port = output_component.output_ports.find_or_initialize_by_name :name => connection_spec[:output_port_name]
|
130
168
|
output_port.save!
|
131
|
-
|
169
|
+
|
132
170
|
input_component = RFlow::Configuration::Component.find_by_name connection_spec[:input_component_name]
|
133
171
|
raise RFlow::Configuration::Component::ComponentNotFound, "#{connection_spec[:input_component_name]}" unless input_component
|
134
172
|
input_port = input_component.input_ports.find_or_initialize_by_name :name => connection_spec[:input_port_name]
|
135
173
|
input_port.save!
|
136
174
|
|
137
|
-
# Create a unique ZMQ address
|
138
|
-
# zmq_address = "ipc://run/rflow.#{output_component.uuid}.#{output_port.uuid}"
|
139
|
-
# if connection_spec[:output_port_key]
|
140
|
-
# zmq_address << ".#{connection_spec[:output_port_key].gsub(/[^\w]/, '').downcase}"
|
141
|
-
# end
|
142
|
-
|
143
175
|
connection = RFlow::Configuration::ZMQConnection.new(:name => connection_spec[:name],
|
144
176
|
:output_port_key => connection_spec[:output_port_key],
|
145
177
|
:input_port_key => connection_spec[:input_port_key])
|
146
|
-
# :options => {
|
147
|
-
# 'output_socket_type' => "PUSH",
|
148
|
-
# 'output_address' => zmq_address,
|
149
|
-
# 'output_responsibility' => "bind",
|
150
|
-
# 'input_socket_type' => "PULL",
|
151
|
-
# 'input_address' => zmq_address,
|
152
|
-
# 'input_responsibility' => "connect",
|
153
|
-
# })
|
154
178
|
|
155
179
|
connection.output_port = output_port
|
156
180
|
connection.input_port = input_port
|
@@ -170,7 +194,7 @@ class RFlow
|
|
170
194
|
raise RFlow::Configuration::Connection::ConnectionInvalid, error_message
|
171
195
|
end
|
172
196
|
|
173
|
-
|
197
|
+
|
174
198
|
# Method called within the config file itself
|
175
199
|
def self.configure
|
176
200
|
config_file = self.new
|
@@ -8,19 +8,20 @@ class RFlow
|
|
8
8
|
|
9
9
|
include ActiveModel::Validations
|
10
10
|
|
11
|
-
|
11
|
+
self.primary_key = 'name'
|
12
|
+
|
12
13
|
attr_accessible :name, :value
|
13
|
-
|
14
|
+
|
14
15
|
DEFAULTS = {
|
15
16
|
'rflow.application_name' => 'rflow',
|
16
|
-
|
17
|
+
|
17
18
|
'rflow.application_directory_path' => '.',
|
18
19
|
'rflow.pid_directory_path' => 'run', #lambda {File.join(Setting['rflow.application_directory_path'], 'run')},
|
19
20
|
'rflow.log_directory_path' => 'log', #lambda {File.join(Setting['rflow.application_directory_path'], 'log')},
|
20
21
|
|
21
22
|
'rflow.log_file_path' => lambda {File.join(Setting['rflow.log_directory_path'], Setting['rflow.application_name'] + '.log')},
|
22
23
|
'rflow.pid_file_path' => lambda {File.join(Setting['rflow.pid_directory_path'], Setting['rflow.application_name'] + '.pid')},
|
23
|
-
|
24
|
+
|
24
25
|
'rflow.log_level' => 'INFO',
|
25
26
|
}
|
26
27
|
|
@@ -41,7 +42,7 @@ class RFlow
|
|
41
42
|
#validate :valid_writable_path, :if => :directory_path?
|
42
43
|
|
43
44
|
# TODO: Think about making this a regex check to pull in other,
|
44
|
-
# externally-defined settings
|
45
|
+
# externally-defined settings
|
45
46
|
def directory_path?
|
46
47
|
DIRECTORY_PATHS.include? self.name
|
47
48
|
end
|
@@ -51,7 +52,7 @@ class RFlow
|
|
51
52
|
errors.add :value, "setting '#{self.name}' is not a directory ('#{File.expand_path self.value}')"
|
52
53
|
end
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def valid_writable_path
|
56
57
|
unless File.writable? self.value
|
57
58
|
errors.add :value, "setting '#{self.name}' is not writable ('#{File.expand_path self.value}')"
|
@@ -61,7 +62,7 @@ class RFlow
|
|
61
62
|
def self.[](setting_name)
|
62
63
|
Setting.find(setting_name).value rescue nil
|
63
64
|
end
|
64
|
-
|
65
|
+
|
65
66
|
end
|
66
67
|
end
|
67
68
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'rflow/configuration/uuid_keyed'
|
3
|
+
|
4
|
+
class RFlow
|
5
|
+
class Configuration
|
6
|
+
|
7
|
+
class Shard < ConfigDB
|
8
|
+
include UUIDKeyed
|
9
|
+
include ActiveModel::Validations
|
10
|
+
|
11
|
+
class ShardInvalid < StandardError; end
|
12
|
+
|
13
|
+
has_many :components, :primary_key => 'uuid', :foreign_key => 'shard_uuid'
|
14
|
+
|
15
|
+
validates_presence_of :name
|
16
|
+
validates_uniqueness_of :name
|
17
|
+
validates_numericality_of :count, :only_integer => true, :greater_than => 0
|
18
|
+
end
|
19
|
+
|
20
|
+
# STI-based classes
|
21
|
+
class ProcessShard < Shard; end
|
22
|
+
class ThreadShard < Shard; end
|
23
|
+
end
|
24
|
+
end
|