ffwd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/bin/ffwd +9 -0
  3. data/bin/fwc +15 -0
  4. data/lib/em/all.rb +68 -0
  5. data/lib/ffwd.rb +250 -0
  6. data/lib/ffwd/channel.rb +62 -0
  7. data/lib/ffwd/circular_buffer.rb +78 -0
  8. data/lib/ffwd/connection.rb +40 -0
  9. data/lib/ffwd/core.rb +173 -0
  10. data/lib/ffwd/core/emitter.rb +38 -0
  11. data/lib/ffwd/core/interface.rb +47 -0
  12. data/lib/ffwd/core/processor.rb +92 -0
  13. data/lib/ffwd/core/reporter.rb +32 -0
  14. data/lib/ffwd/debug.rb +76 -0
  15. data/lib/ffwd/debug/connection.rb +48 -0
  16. data/lib/ffwd/debug/monitor_session.rb +71 -0
  17. data/lib/ffwd/debug/tcp.rb +82 -0
  18. data/lib/ffwd/event.rb +65 -0
  19. data/lib/ffwd/event_emitter.rb +57 -0
  20. data/lib/ffwd/handler.rb +43 -0
  21. data/lib/ffwd/lifecycle.rb +92 -0
  22. data/lib/ffwd/logging.rb +139 -0
  23. data/lib/ffwd/metric.rb +55 -0
  24. data/lib/ffwd/metric_emitter.rb +50 -0
  25. data/lib/ffwd/plugin.rb +149 -0
  26. data/lib/ffwd/plugin/json_line.rb +47 -0
  27. data/lib/ffwd/plugin/json_line/connection.rb +118 -0
  28. data/lib/ffwd/plugin/log.rb +35 -0
  29. data/lib/ffwd/plugin/log/writer.rb +42 -0
  30. data/lib/ffwd/plugin_channel.rb +64 -0
  31. data/lib/ffwd/plugin_loader.rb +121 -0
  32. data/lib/ffwd/processor.rb +96 -0
  33. data/lib/ffwd/processor/count.rb +109 -0
  34. data/lib/ffwd/processor/histogram.rb +200 -0
  35. data/lib/ffwd/processor/rate.rb +116 -0
  36. data/lib/ffwd/producing_client.rb +181 -0
  37. data/lib/ffwd/protocol.rb +28 -0
  38. data/lib/ffwd/protocol/tcp.rb +126 -0
  39. data/lib/ffwd/protocol/tcp/bind.rb +64 -0
  40. data/lib/ffwd/protocol/tcp/connection.rb +107 -0
  41. data/lib/ffwd/protocol/tcp/flushing_connect.rb +135 -0
  42. data/lib/ffwd/protocol/tcp/plain_connect.rb +74 -0
  43. data/lib/ffwd/protocol/udp.rb +48 -0
  44. data/lib/ffwd/protocol/udp/bind.rb +64 -0
  45. data/lib/ffwd/protocol/udp/connect.rb +110 -0
  46. data/lib/ffwd/reporter.rb +65 -0
  47. data/lib/ffwd/retrier.rb +72 -0
  48. data/lib/ffwd/schema.rb +92 -0
  49. data/lib/ffwd/schema/default.rb +36 -0
  50. data/lib/ffwd/schema/spotify100.rb +58 -0
  51. data/lib/ffwd/statistics.rb +29 -0
  52. data/lib/ffwd/statistics/collector.rb +99 -0
  53. data/lib/ffwd/statistics/system_statistics.rb +255 -0
  54. data/lib/ffwd/tunnel.rb +27 -0
  55. data/lib/ffwd/tunnel/plugin.rb +47 -0
  56. data/lib/ffwd/tunnel/tcp.rb +60 -0
  57. data/lib/ffwd/tunnel/udp.rb +61 -0
  58. data/lib/ffwd/utils.rb +46 -0
  59. data/lib/ffwd/version.rb +18 -0
  60. data/lib/fwc.rb +206 -0
  61. metadata +163 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1d0b671545d89979b849e559f5a5e5c6c0a86d8a
4
+ data.tar.gz: 3470a5c756d427bac159f4ea1c2c5a45099f87ef
5
+ SHA512:
6
+ metadata.gz: 84bfcbee1c667878c8760b3c1722c8d3b3a822d8d1f23d4c0e25e0bde744d815c4d74f7d3f6baf4a52be058b2d8c18a5506117f51e29e8e7e6c0236dd3b2ab11
7
+ data.tar.gz: 0e97592bd405068ae86f0d6057cc9d8d0989b310858b7e070a53dfc0c583c71de538edf12086c60658f768191423f2be5b02d38ff4ec98cf5ca078fcdfd20ef1
data/bin/ffwd ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if __FILE__ == $0
4
+ lib = File.expand_path File.join('..', '..', 'lib'), $0
5
+ $:.insert(0, lib) if File.file? File.join(lib, 'ffwd.rb')
6
+ end
7
+
8
+ require 'ffwd'
9
+ exit FFWD::main(ARGV || [])
data/bin/fwc ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if __FILE__ == $0
4
+ lib = File.expand_path File.join('..', '..', 'lib'), $0
5
+ $:.insert(0, lib) if File.file? File.join(lib, 'ffwd.rb')
6
+ end
7
+
8
+ begin
9
+ require 'fwc'
10
+ rescue LoadError
11
+ require 'rubygems'
12
+ require 'fwc'
13
+ end
14
+
15
+ exit FWC::main(ARGV || [])
data/lib/em/all.rb ADDED
@@ -0,0 +1,68 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ # EventMachine extension to create a deferrable that correctly manages multiple
17
+ # deferred objects in parallel.
18
+
19
+ module EM
20
+ class All
21
+ include EM::Deferrable
22
+
23
+ def initialize
24
+ @defs = []
25
+ @errors = []
26
+ @results = []
27
+ @all_ok = true
28
+ @any_ok = false
29
+ @done = 0
30
+ end
31
+
32
+ def <<(d)
33
+ @defs << d
34
+
35
+ index = @results.size
36
+
37
+ @results << nil
38
+ @errors << nil
39
+
40
+ d.callback do |*args|
41
+ @results[index] = args
42
+ @any_ok = true
43
+ check_results
44
+ end
45
+
46
+ d.errback do |*args|
47
+ @errors[index] = args
48
+ @all_ok = false
49
+ check_results
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def check_results
56
+ @done += 1
57
+
58
+ return unless @defs.size == @done
59
+
60
+ unless @all_ok
61
+ fail(@errors)
62
+ return
63
+ end
64
+
65
+ succeed(@results)
66
+ end
67
+ end
68
+ end
data/lib/ffwd.rb ADDED
@@ -0,0 +1,250 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require 'yaml'
17
+ require 'optparse'
18
+
19
+ require_relative 'ffwd/core'
20
+ require_relative 'ffwd/logging'
21
+ require_relative 'ffwd/plugin'
22
+ require_relative 'ffwd/plugin_loader'
23
+ require_relative 'ffwd/processor'
24
+ require_relative 'ffwd/schema'
25
+
26
+ module FFWD
27
+ DEFAULT_PLUGIN_DIRECTORIES = [
28
+ './plugins'
29
+ ]
30
+
31
+ def self.load_yaml path
32
+ return YAML.load_file path
33
+ rescue => e
34
+ log.error "Failed to load config: #{path} (#{e})"
35
+ return nil
36
+ end
37
+
38
+ def self.merge_configurations target, source
39
+ if target.is_a? Hash
40
+ raise "source not a Hash: #{source}" unless source.is_a? Hash
41
+
42
+ source.each do |key, value|
43
+ target[key] = merge_configurations target[key], source[key]
44
+ end
45
+
46
+ return target
47
+ end
48
+
49
+ if target.is_a? Array
50
+ raise "source not an Array: #{source}" unless source.is_a? Array
51
+ return target + source
52
+ end
53
+
54
+ # override source
55
+ return source
56
+ end
57
+
58
+ def self.load_config_dir dir, config
59
+ Dir.entries(dir).sort.each do |entry|
60
+ entry_path = File.join dir, entry
61
+
62
+ next unless File.file? entry_path
63
+
64
+ if entry.start_with? "."
65
+ log.debug "Ignoring: #{entry_path} (hidden file)"
66
+ next
67
+ end
68
+
69
+ c = load_yaml entry_path
70
+
71
+ if c.nil?
72
+ log.warn "Ignoring: #{entry_path} (invalid yaml)"
73
+ next
74
+ end
75
+
76
+ yield c
77
+ end
78
+ end
79
+
80
+ def self.opts
81
+ @@opts ||= {:debug => false, :config => nil, :config_dir => nil,
82
+ :list_plugins => false, :list_schemas => false,
83
+ :dump_config => false,
84
+ :plugin_directories => DEFAULT_PLUGIN_DIRECTORIES}
85
+ end
86
+
87
+ def self.parser
88
+ @@parser ||= OptionParser.new do |o|
89
+ o.banner = "Usage: ffwd [options]"
90
+
91
+ o.on "-d", "--[no-]debug" do |d|
92
+ opts[:debug] = d
93
+ end
94
+
95
+ o.on "-c", "--config <path>" do |path|
96
+ opts[:config_path] = path
97
+ end
98
+
99
+ o.on "-d", "--config-dir <path>" do |path|
100
+ opts[:config_dir] = path
101
+ end
102
+
103
+ o.on "--list-plugins", "Print available plugins." do
104
+ opts[:list_plugins] = true
105
+ end
106
+
107
+ o.on "--list-schemas", "Print available schemas." do
108
+ opts[:list_schemas] = true
109
+ end
110
+
111
+ o.on "--dump-config", "Dump the configuration that has been loaded." do
112
+ opts[:dump_config] = true
113
+ end
114
+
115
+ o.on "--plugin-directory <dir>", "Load plugins from the specified directory." do |dir|
116
+ opts[:plugin_directories] << dir
117
+ end
118
+ end
119
+ end
120
+
121
+ def self.parse_options args
122
+ parser.parse args
123
+ end
124
+
125
+ def self.setup_plugins config
126
+ plugins = {}
127
+
128
+ plugins[:tunnel] = FFWD::Plugin.load_plugins(
129
+ log, "Tunnel", config[:tunnel], :tunnel)
130
+
131
+ plugins[:input] = FFWD::Plugin.load_plugins(
132
+ log, "Input", config[:input], :input)
133
+
134
+ plugins[:output] = FFWD::Plugin.load_plugins(
135
+ log, "Output", config[:output], :output)
136
+
137
+ plugins
138
+ end
139
+
140
+ def self.main args
141
+ parse_options args
142
+
143
+ FFWD.log_config[:level] = opts[:debug] ? Logger::DEBUG : Logger::INFO
144
+
145
+ config = {:debug => nil}
146
+
147
+ if config_path = opts[:config_path]
148
+ unless File.file? config_path
149
+ puts "Configuration path does not exist: #{config_path}"
150
+ puts ""
151
+ puts parser.help
152
+ return 1
153
+ end
154
+
155
+ unless source = load_yaml(config_path)
156
+ return 0
157
+ end
158
+
159
+ merge_configurations config, source
160
+ end
161
+
162
+ if config_dir = opts[:config_dir]
163
+ unless File.directory? config_dir
164
+ puts "Configuration directory does not exist: #{path}"
165
+ puts ""
166
+ puts parser.help
167
+ return 1
168
+ end
169
+
170
+ load_config_dir(config_dir, config) do |c|
171
+ merge_configurations config, c
172
+ end
173
+ end
174
+
175
+ if config[:logging]
176
+ config[:logging].each do |key, value|
177
+ FFWD.log_config[key] = value
178
+ end
179
+ end
180
+
181
+ FFWD.log_reload
182
+
183
+ if FFWD.log_config[:file]
184
+ puts "Logging to file: #{FFWD.log_config[:file]}"
185
+ end
186
+
187
+ blacklist = config[:blacklist] || {}
188
+
189
+ directories = ((config[:plugin_directories] || []) +
190
+ (opts[:plugin_directories] || []))
191
+
192
+ PluginLoader.plugin_directories = directories
193
+
194
+ PluginLoader.load FFWD::Plugin, blacklist[:plugins] || []
195
+ PluginLoader.load FFWD::Processor, blacklist[:processors] || []
196
+ PluginLoader.load FFWD::Schema, blacklist[:schemas] || []
197
+
198
+ stop_early = false
199
+ stop_early ||= opts[:list_plugins]
200
+ stop_early ||= opts[:list_schemas]
201
+ stop_early ||= opts[:dump_config]
202
+
203
+ plugins = setup_plugins config
204
+
205
+ if opts[:list_plugins]
206
+ puts "Available Plugins:"
207
+
208
+ FFWD::Plugin.loaded.each do |name, plugin|
209
+ puts "Plugin '#{name}' (#{plugin.source})"
210
+ puts " supports: #{plugin.capabilities.join(' ')}"
211
+ end
212
+
213
+ puts "Activated Plugins:"
214
+
215
+ plugins.each do |kind, kind_plugins|
216
+ puts "#{kind}:"
217
+
218
+ if kind_plugins.empty?
219
+ puts " (no active)"
220
+ end
221
+
222
+ kind_plugins.each do |p|
223
+ puts " #{p.name}: #{p.config}"
224
+ end
225
+ end
226
+ end
227
+
228
+ if opts[:list_schemas]
229
+ puts "Available Schemas:"
230
+
231
+ FFWD::Schema.loaded.each do |key, schema|
232
+ name, content_type = key
233
+ puts "Schema '#{name}' #{content_type} (#{schema.source})"
234
+ end
235
+ end
236
+
237
+ if opts[:dump_config]
238
+ puts "Dumping Configuration:"
239
+ puts config
240
+ end
241
+
242
+ if stop_early
243
+ return 0
244
+ end
245
+
246
+ core = FFWD::Core.new plugins, config
247
+ core.run
248
+ return 0
249
+ end
250
+ end
@@ -0,0 +1,62 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ require 'set'
17
+
18
+ # Defines containers which are limited in size.
19
+ module FFWD
20
+ class Channel
21
+ class Sub
22
+ attr_reader :channel, :block
23
+
24
+ def initialize channel, block
25
+ @channel = channel
26
+ @block = block
27
+ end
28
+
29
+ def unsubscribe
30
+ @channel.unsubscribe self
31
+ end
32
+ end
33
+
34
+ attr_reader :subs
35
+
36
+ def initialize log, name
37
+ @log = log
38
+ @name = name
39
+ @subs = Set.new
40
+ end
41
+
42
+ def <<(item)
43
+ @subs.each do |sub|
44
+ begin
45
+ sub.block.call item
46
+ rescue => e
47
+ @log.error "#{@name}: Subscription failed", e
48
+ end
49
+ end
50
+ end
51
+
52
+ def subscribe(&block)
53
+ s = Sub.new(self, block)
54
+ @subs << s
55
+ return s
56
+ end
57
+
58
+ def unsubscribe sub
59
+ @subs.delete sub
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,78 @@
1
+ # $LICENSE
2
+ # Copyright 2013-2014 Spotify AB. All rights reserved.
3
+ #
4
+ # The contents of this file are licensed under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with the
6
+ # License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ # License for the specific language governing permissions and limitations under
14
+ # the License.
15
+
16
+ Node = Struct.new(:value, :next)
17
+
18
+ module FFWD
19
+ class CircularBuffer
20
+ attr_reader :buffer, :capacity, :size
21
+
22
+ def initialize capacity
23
+ @capacity = capacity
24
+ @first = nil
25
+ @last = nil
26
+ @size = 0
27
+ end
28
+
29
+ def clear
30
+ @first = nil
31
+ @last = nil
32
+ @size = 0
33
+ end
34
+
35
+ def empty?
36
+ size == 0
37
+ end
38
+
39
+ def peek
40
+ return nil if @first.nil?
41
+ @first.value
42
+ end
43
+
44
+ def shift
45
+ return nil if @first.nil?
46
+ value = @first.value
47
+ @first = @first.next
48
+ @last = nil if @first.nil?
49
+ @size -= 1
50
+ value
51
+ end
52
+
53
+ def << item
54
+ node = Node.new(item, nil)
55
+
56
+ if @last.nil?
57
+ @first = @last = node
58
+ else
59
+ @last = @last.next = node
60
+ end
61
+
62
+ if @size >= @capacity
63
+ @first = @first.next
64
+ else
65
+ @size += 1
66
+ end
67
+ end
68
+
69
+ def each
70
+ current = @first
71
+
72
+ while not current.nil?
73
+ yield current.value
74
+ current = current.next
75
+ end
76
+ end
77
+ end
78
+ end