ffwd 0.1.0

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 (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