pushyd 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f1475888d98b181ea4d70fbd0568a1ea73b900a
4
- data.tar.gz: a4f3e851e8d8698084e480a2ea94c2fd1040d5dc
3
+ metadata.gz: 838aaf0ca927c20acf4d53714ff5eaa11499716a
4
+ data.tar.gz: e481c674723737cf9b8b6f84b38e57c8e6b39804
5
5
  SHA512:
6
- metadata.gz: 507f389e40d4d80de6f4db4ed61c526d644cc0c0bdce04b21565c3507289421f0bda9d650f6dd4cfb3b3f9e6ebe0d491a6410781bd18de1b34fd8d9160a30375
7
- data.tar.gz: c999e4e4bb98169092434000ceaa5aaf6facb2be792123ae56170458b732de78d2351c30883fabdd4f7ef5eb4fd45b776b823986546cc0811ea961b9d5c1f0c3
6
+ metadata.gz: e7cad3480998b54851e30573b0e1f8229c9c2ccd50359436b11b236ccccbd6fc2817e7a7bcecbcedbff0538366efa5814c61aeefccedc3d1d2408c2e07104fcb
7
+ data.tar.gz: 37cc4b0233a4da19f28d3c2a52cce679e4e12129b282bdb3d514ceee0a1ee5dac3a40b051bb5f783f20aee425eab2a0fa4b06003234030ad037e16dec4fe9813
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pushyd (0.0.2)
4
+ pushyd (0.1.1)
5
5
  bunny
6
6
  chamber
7
7
  daemons
@@ -10,14 +10,14 @@ PATH
10
10
  terminal-table
11
11
 
12
12
  GEM
13
- remote: http://rubygems.org/
13
+ remote: https://rubygems.org/
14
14
  specs:
15
15
  addressable (2.4.0)
16
16
  amq-protocol (2.0.1)
17
17
  ast (2.2.0)
18
18
  astrolabe (1.3.1)
19
19
  parser (~> 2.2)
20
- bunny (2.2.2)
20
+ bunny (2.3.1)
21
21
  amq-protocol (>= 2.0.1)
22
22
  chamber (2.9.0)
23
23
  hashie (~> 3.3)
data/bin/pushyd.rb CHANGED
@@ -10,15 +10,18 @@ begin
10
10
  rescue LoadError
11
11
  raise "EXITING: some basic libs were not found"
12
12
  end
13
+ include PushyDaemon
13
14
 
14
- # Guess app root
15
- APP_ROOT = File.expand_path(File.dirname(__FILE__) + "/../")
16
15
 
17
- # Parse options and check compliance
18
- cmd_config = nil
19
- cmd_env = "production"
20
- cmd_dump = false
16
+ # Handle configuration
17
+ APP_ROOT = File.expand_path(File.dirname(__FILE__) + "/../")
21
18
  begin
19
+ # Defaults
20
+ cmd_config = nil
21
+ cmd_env = "production"
22
+ cmd_dump = false
23
+
24
+ # Parse options and check compliance
22
25
  OptionParser.new do |opts|
23
26
  opts.banner = "Usage: #{File.basename $PROGRAM_NAME} [options] start|stop"
24
27
  opts.on("-c", "--config CONFIGFILE") { |config| cmd_config = File.expand_path(config)}
@@ -26,21 +29,25 @@ begin
26
29
  opts.on("-d", "--dump") { cmd_dump = true }
27
30
  opts.on("", "--dev") { cmd_env = "development" }
28
31
  end.order!(ARGV)
29
- rescue OptionParser::InvalidOption => e
30
- abort "EXITING: option parser: #{e.message}"
31
- end
32
32
 
33
+ # Build Chamber-based configuration from Gemspec with initial context
34
+ Config.prepare root: APP_ROOT, gemspec: "pushyd", env: cmd_env, config: cmd_config
33
35
 
34
- # Build Chamber-based configuration from Gemspec with initial context
35
- Config.prepare root: APP_ROOT, gemspec: "pushyd", env: cmd_env, config: cmd_config
36
+ # Display final configuration
37
+ puts "--- #{Config.name} #{Config.version}"
38
+ puts "Environment \t #{Config.env}"
39
+ puts "Config files \t #{Config.files}"
40
+ puts
41
+ puts "Log file \t #{Config[:log]}"
42
+ puts Config.dump if cmd_dump
36
43
 
37
- # Display final configuration
38
- puts "--- #{Config.name} #{Config.version}"
39
- puts "Environment \t #{Config.env}"
40
- puts "Config files \t #{Config.files}"
41
- puts
42
- puts "Log file \t #{Config[:log]}"
43
- puts Config.dump if cmd_dump
44
+ rescue OptionParser::InvalidOption => e
45
+ abort "EXITING: option parser: #{e.message}"
46
+ rescue PushyDaemon::ConfigParseError => e
47
+ abort "EXITING: ConfigParseError: #{e.message}"
48
+ rescue Exception => e
49
+ abort "EXITING: Exception: #{e.message}"
50
+ end
44
51
 
45
52
 
46
53
  # Run daemon
data/lib/pushyd/config.rb CHANGED
@@ -1,43 +1,59 @@
1
1
  require "chamber"
2
2
 
3
- class Config
4
- extend Chamber
5
-
6
- class << self
7
- attr_reader :name
8
- attr_reader :spec
9
- attr_reader :files
10
- attr_reader :version
11
- attr_reader :env
12
- end
3
+ module PushyDaemon
13
4
 
14
- def self.prepare args = {}
15
- # Context parameters
16
- raise "config: missing root" unless (@root = args[:root])
17
- raise "config: missing env" unless (@env = args[:env])
18
-
19
- # Gemspec parameter
20
- gemspec_path = "#{args[:root]}/#{args[:gemspec]}.gemspec"
21
- raise "config: missing gemspec" unless args[:gemspec]
22
- raise "config: missing gemspec file at #{gemspec_path}" unless File.exist?(gemspec_path)
23
-
24
- # Load Gemspec
25
- @spec = Gem::Specification::load gemspec_path
26
- @name = @spec.name
27
- @version = @spec.version
28
- raise "config: missing name" unless @name
29
-
30
- # Init Chamber (defaults, etc, cmdline)
31
- @files = ["#{args[:root]}/defaults.yml"]
32
- @files << File.expand_path("/etc/#{@name}.yml")
33
- @files << args[:config].to_s if args[:config]
34
-
35
- # Load configuration files
36
- load files: @files, namespaces: { environment: @env }
37
- end
5
+ class ConfigMissingParameter < StandardError; end
6
+ class ConfigParseError < StandardError; end
38
7
 
39
- def self.dump
40
- self.to_hash.to_yaml
41
- end
8
+ class Config
9
+ extend Chamber
10
+
11
+ class << self
12
+ attr_reader :name
13
+ attr_reader :spec
14
+ attr_reader :files
15
+ attr_reader :version
16
+ attr_reader :env
17
+ end
18
+
19
+ def self.prepare args = {}
20
+ # Context parameters
21
+ raise PushyDaemon::ConfigMissingParameter, "missing root" unless (@root = args[:root])
22
+ raise PushyDaemon::ConfigMissingParameter, "missing env" unless (@env = args[:env])
23
+
24
+ # Gemspec parameter
25
+ gemspec_path = "#{args[:root]}/#{args[:gemspec]}.gemspec"
26
+ raise PushyDaemon::ConfigMissingParameter, "missing gemspec" unless args[:gemspec]
27
+ raise PushyDaemon::ConfigMissingParameter, "gemspec file not found: #{gemspec_path}" unless File.exist?(gemspec_path)
28
+
29
+ # Load Gemspec
30
+ @spec = Gem::Specification::load gemspec_path
31
+ @name = @spec.name
32
+ @version = @spec.version
33
+ raise PushyDaemon::ConfigMissingParameter, "missing name" unless @name
34
+
35
+ # Init Chamber (defaults, etc, cmdline)
36
+ @files = ["#{args[:root]}/defaults.yml"]
37
+ @files << File.expand_path("/etc/#{@name}.yml")
38
+ @files << args[:config].to_s if args[:config]
42
39
 
40
+ # Load configuration files
41
+ load files: @files, namespaces: { environment: @env }
42
+
43
+ # Try to access any key to force parsing of the files
44
+ self[:dummy]
45
+
46
+ rescue Psych::SyntaxError => e
47
+ raise PushyDaemon::ConfigParseError, e.message
48
+
49
+ rescue Exception => e
50
+ raise PushyDaemon::ConfigParseError, e.message
51
+
52
+ end
53
+
54
+ def self.dump
55
+ self.to_hash.to_yaml
56
+ end
57
+
58
+ end
43
59
  end
@@ -0,0 +1,13 @@
1
+ # Constants: global
2
+ WAY_OUT = "SENT"
3
+ WAY_IN = "RECD"
4
+ WAY_POST = "POST"
5
+
6
+
7
+ # Constants: proxy
8
+ PROXY_MESSAGE_MAX = 1
9
+ PROXY_USE_ACK = false
10
+ PROXY_SCOPE = "dev"
11
+
12
+
13
+ # Constants: shouter
data/lib/pushyd/daemon.rb CHANGED
@@ -5,16 +5,14 @@ module PushyDaemon
5
5
  # Create a new proxy
6
6
  p = Proxy.new(logger)
7
7
 
8
- # Prepare subscriptions
9
- p.prepare
10
-
11
- # Make it listen
12
-
13
8
  # Dump config table
14
9
  puts p.table.to_s
15
10
 
11
+ # Create a new shouter
12
+ s = Shouter.new(logger)
13
+
16
14
  # Start infinite loop
17
- p.main
15
+ s.shout
18
16
  end
19
17
 
20
18
  end
@@ -0,0 +1,126 @@
1
+ require 'bunny'
2
+ require 'securerandom'
3
+
4
+ module PushyDaemon
5
+
6
+ class EndpointConnexionContext < StandardError; end
7
+ class EndpointConnectionError < StandardError; end
8
+ class EndpointSubscribeContext < StandardError; end
9
+ class EndpointSubscribeError < StandardError; end
10
+
11
+ class Endpoint
12
+
13
+ def initialize(logger)
14
+ @logger = logger
15
+ end
16
+
17
+ protected
18
+
19
+ def abort message
20
+ @logger.error "ABORT #{self.class}: #{message}"
21
+ raise "ABORT #{self.class}: #{message}"
22
+ end
23
+
24
+ def info message
25
+ @logger.info "#{self.class}: #{message}"
26
+ end
27
+
28
+ def message params = {}
29
+ # Indenting
30
+ indent = " " * (params[:way].length)
31
+
32
+ # Header
33
+ @logger.info sprintf(
34
+ "%3s %-15s %s",
35
+ params[:way],
36
+ params[:exchange],
37
+ params[:key]
38
+ )
39
+
40
+ # Attributes
41
+ if (params[:attrs].is_a? Hash)
42
+ params[:attrs].each do |name, value|
43
+ @logger.info sprintf("%s %-15s %s", indent, name, value)
44
+ end
45
+ end
46
+
47
+ # Body (split in lines to log them separately)
48
+ unless (params[:body].nil? || params[:body].empty?)
49
+ JSON.pretty_generate(params[:body]).each_line do |line|
50
+ @logger.info sprintf("%s %s", indent, line.rstrip)
51
+ end
52
+ end
53
+ end
54
+
55
+ # Start connexion to RabbitMQ
56
+ def connect busconf
57
+ raise PushyDaemon::EndpointConnexionContext, "invalid bus host/port" unless (busconf.is_a? Hash) &&
58
+ busconf[:host] && busconf[:port]
59
+
60
+ puts "connecting to #{busconf[:host]} port #{busconf[:port]}"
61
+ conn = Bunny.new host: (busconf[:host].to_s || "localhost").to_s,
62
+ port: busconf[:port].to_i,
63
+ user: busconf[:user].to_s,
64
+ pass: busconf[:pass].to_s,
65
+ heartbeat: :server
66
+ conn.start
67
+ rescue Bunny::TCPConnectionFailedForAllHosts, Bunny::AuthenticationFailureError, AMQ::Protocol::EmptyResponseError => e
68
+ raise PushyDaemon::EndpointConnectionError, "error connecting (#{e.class})"
69
+ rescue Exception => e
70
+ raise PushyDaemon::EndpointConnectionError, "unknow (#{e.inspect})"
71
+ else
72
+ return conn
73
+ end
74
+
75
+ # Declare or return the exchange for this topic
76
+ def channel_exchange topic
77
+ @exchanges ||= {}
78
+ @exchanges[topic] ||= @channel.topic(topic, durable: true, persistent: true)
79
+ end
80
+
81
+ # Subscribe to interesting topic/routes and bind a listenner
82
+ def channel_subscribe rule
83
+ # Check information
84
+ rule_name = rule[:name].to_s
85
+ rule_topic = rule[:topic].to_s
86
+ rule_routes = rule[:routes].to_s.split(' ')
87
+ rule_queue = "#{Config.name}-#{PROXY_SCOPE}-#{rule[:name]}"
88
+ raise PushyDaemon::EndpointSubscribeContext, "rule [#{rule_name}] lacking topic" unless rule_topic
89
+ raise PushyDaemon::EndpointSubscribeContext, "rule [#{rule_name}] lacking routes" if rule_routes.empty?
90
+
91
+ # Create queue for this rule (remove it beforehand)
92
+ #conn.create_channel.queue_delete(rule_queue_name)
93
+ queue = @channel.queue(rule_queue, auto_delete: false, durable: true)
94
+
95
+ # Bind each route from this topic-exchange
96
+ topic_exchange = channel_exchange(rule_topic)
97
+ rule_routes.each do |route|
98
+ # Bind exchange to queue
99
+ queue.bind topic_exchange, routing_key: route
100
+ info "subscribe: bind [#{rule_topic}/#{route}] \t> #{rule_queue}"
101
+
102
+ # Add row to config table
103
+ @table.add_row [rule_name, rule_topic, route, rule[:relay].to_s, rule[:title].to_s ]
104
+ end
105
+
106
+ # Subscribe to our new queue
107
+ queue.subscribe(block: false, manual_ack: PROXY_USE_ACK, message_max: PROXY_MESSAGE_MAX) do |delivery_info, metadata, payload|
108
+
109
+ # Handle the message
110
+ handle_message rule, delivery_info, metadata, payload
111
+
112
+ end
113
+
114
+ rescue Bunny::PreconditionFailed => e
115
+ raise PushyDaemon::EndpointSubscribeError, "PreconditionFailed: [#{rule_topic}] code(#{e.channel_close.reply_code}) message(#{e.channel_close.reply_text})"
116
+
117
+ rescue Exception => e
118
+ raise PushyDaemon::EndpointSubscribeError, "unhandled (#{e.inspect})"
119
+
120
+ end
121
+
122
+ def handle_message rule, delivery_info, metadata, payload
123
+ end
124
+
125
+ end
126
+ end
data/lib/pushyd/proxy.rb CHANGED
@@ -1,25 +1,18 @@
1
1
  require 'rest_client'
2
- require 'bunny'
3
2
  require 'yaml'
4
3
  require 'json'
5
4
  require 'terminal-table'
6
5
 
7
- # Constants
8
- PROXY_MESSAGE_MAX = 1
9
- PROXY_USE_ACK = false
10
- PROXY_SCOPE = "dev"
11
- # PROXY_IDENT = "proxy"
12
- # QUEUE_HOST = `hostname`.to_s.chomp
13
- # SEPARATOR = "="*160
14
- # ACK_PERCENT = 50
6
+ module PushyDaemon
15
7
 
8
+ # class ProxyConnexionContext < StandardError; end
16
9
 
17
- module PushyDaemon
18
- class Proxy
10
+ class Proxy < Endpoint
19
11
 
20
12
  attr_accessor :table
21
13
 
22
14
  def initialize(logger)
15
+ # Init
23
16
  @exchanges = {}
24
17
  @logger = logger
25
18
 
@@ -28,123 +21,41 @@ module PushyDaemon
28
21
  @table.title = "Propagation rules"
29
22
  @table.headings = ["queue binding", "topic", "route", "relay", "title"]
30
23
  @table.align_column(5, :right)
31
- end
32
24
 
33
- def prepare
34
25
  # Start connexion to RabbitMQ and create channel
35
26
  conn = connect Config.bus
36
27
  @channel = conn.create_channel
37
- info "prepare: connected on a channel"
28
+ info "connected on a channel"
38
29
 
39
- # Check rules
40
- unless (Config.rules.is_a? Enumerable) && !Config.rules.empty?
30
+ # Check config
31
+ config_rules = Config[:rules]
32
+ unless (config_rules.is_a? Enumerable) && !config_rules.empty?
41
33
  abort "prepare: empty [rules] section"
42
34
  end
43
- info "prepare: found [#{Config.rules.size}] rules"
35
+ info "found rules: #{config_rules.keys.join(', ')}"
44
36
 
45
37
  # Subsribe for each and every rule/route
46
- Config.rules.each do |name, rule|
38
+ config_rules.each do |name, rule|
47
39
  rule[:name] = name
48
40
  channel_subscribe rule
49
41
  #abort "prepare: OK"
50
42
  end
51
43
 
52
44
  # Send config table to logs
53
- info "prepare: dumping configuration\n#{@table.to_s}"
54
- end
45
+ info "dumping configuration\n#{@table.to_s}"
55
46
 
56
- def main
57
- loop do
58
- info "ping"
59
- sleep(1)
60
- end
47
+ rescue Bunny::TCPConnectionFailedForAllHosts => e
48
+ abort "ERROR: cannot connect to RabbitMQ hosts (#{e.inspect})"
61
49
  end
62
50
 
63
51
  private
64
52
 
65
- def abort message
66
- @logger.error "ABORT: #{message}"
67
- raise "ABORT: #{message}"
68
- end
69
-
70
- def info message
71
- @logger.info message
72
- end
73
-
74
- def dump_rules rules
75
-
76
- end
77
-
78
- # Start connexion to RabbitMQ
79
- def connect busconf
80
- abort "connect: bus host/port not found" unless busconf.is_a? Hash
81
-
82
- puts "connecting to #{busconf[:host]} port #{busconf[:port]}"
83
- conn = Bunny.new host: (busconf[:host].to_s || "localhost").to_s,
84
- port: busconf[:port].to_i,
85
- user: busconf[:user].to_s,
86
- pass: busconf[:pass].to_s,
87
- heartbeat: :server
88
- conn.start
89
- rescue Bunny::TCPConnectionFailedForAllHosts, Bunny::AuthenticationFailureError, AMQ::Protocol::EmptyResponseError => e
90
- abort "connect: error connecting to RabbitMQ (#{e.class})"
91
- rescue Exception => e
92
- abort "connect: unknow connection error (#{e.inspect})"
93
- else
94
- return conn
95
- end
96
-
97
- # Declare or return the exchange for this topic
98
- def channel_exchange topic
99
- @exchanges ||= {}
100
- @exchanges[topic] ||= @channel.topic(topic, durable: true, persistent: true)
101
- end
102
-
103
- # Subscribe to interesting topic/routes and bind a listenner
104
- def channel_subscribe rule
105
- # Check information
106
- rule_name = rule[:name].to_s
107
- rule_topic = rule[:topic].to_s
108
- rule_routes = rule[:routes].to_s.split(' ')
109
- rule_queue = "#{Config.name}-#{PROXY_SCOPE}-#{rule[:name]}"
110
- abort "subscribe: rule [#{rule_name}] lacking topic" unless rule_topic
111
- abort "subscribe: rule [#{rule_name}] lacking routes" if rule_routes.empty?
112
-
113
- # Create queue for this rule (remove it beforehand)
114
- #conn.create_channel.queue_delete(rule_queue_name)
115
- queue = @channel.queue(rule_queue, auto_delete: false, durable: true)
116
-
117
- # Bind each route from this topic-exchange
118
- topic_exchange = channel_exchange(rule_topic)
119
- rule_routes.each do |route|
120
- # Bind exchange to queue
121
- queue.bind topic_exchange, routing_key: route
122
- info "subscribe: bind: \t[#{rule_topic}] \t[#{route}] \t> [#{rule_queue}]"
123
-
124
- # Add row to config table
125
- @table.add_row [rule_name, rule_topic, route, rule[:relay].to_s, rule[:title].to_s ]
126
- end
127
-
128
- # Subscribe to our new queue
129
- queue.subscribe(block: false, manual_ack: PROXY_USE_ACK, message_max: PROXY_MESSAGE_MAX) do |delivery_info, metadata, payload|
130
-
131
- # Handle the message
132
- handle_message rule[:name], rule, delivery_info, metadata, payload
133
-
134
- end
135
-
136
- rescue Bunny::PreconditionFailed => e
137
- abort "subscribe: PreconditionFailed: [#{rule_topic}] code(#{e.channel_close.reply_code}) message(#{e.channel_close.reply_text})"
138
- rescue Exception => e
139
- abort "subscribe: unhandled (#{e.inspect})"
140
-
141
- end
142
-
143
53
  # Handle the reception of a message on a queue
144
54
  def handle_message rule, delivery_info, metadata, payload
145
55
  # Prepare data
146
56
  rule_name = rule[:name]
147
- msg_topic = delivery_info.exchange
57
+ rule_relay = rule[:relay]
58
+ msg_exchange = delivery_info.exchange
148
59
  msg_rkey = delivery_info.routing_key.force_encoding('UTF-8')
149
60
  msg_headers = metadata.headers || {}
150
61
 
@@ -152,38 +63,43 @@ module PushyDaemon
152
63
  data = parse payload, metadata.content_type #, rule
153
64
 
154
65
  # Announce match
155
- header rule_name, "<", msg_topic, msg_rkey
66
+ message way: WAY_IN,
67
+ exchange: msg_exchange,
68
+ key: msg_rkey,
69
+ body: data,
70
+ attrs: {
71
+ 'rule' => rule_name,
72
+ 'app-id' => metadata.app_id,
73
+ 'content-type' => metadata.content_type,
74
+ }
156
75
 
157
76
  # Build notification payload
158
- body = {
159
- # received: msg_topic,
160
- exchange: msg_topic,
77
+ post_body = {
78
+ exchange: msg_exchange,
161
79
  route: msg_rkey,
162
- #headers: msg_headers,
163
80
  sent_at: msg_headers['sent_at'],
164
81
  sent_by: msg_headers['sent_by'],
165
82
  data: data,
166
83
  }
167
- pretty_body = JSON.pretty_generate(body)
168
-
169
- # Dump body data
170
- puts "RULE: #{rule.inspect}"
171
- puts "APP-ID: #{metadata.app_id}"
172
- puts "CONTENT-TYPE: #{metadata.content_type}"
173
- puts pretty_body
174
84
 
175
85
  # Propagate data if needed
176
- #propagate rule[:relay], pretty_body
86
+ propagate rule_relay, post_body if rule_relay
177
87
  end
178
88
 
179
- def propagate url, body
89
+ def propagate relay_url, post_body
180
90
  # Nothing more to do if no relay
181
- return if url.nil? || url.empty?
91
+ return if relay_url.nil? || relay_url.empty?
92
+ id = SecureRandom.random_number(100)
93
+
94
+ # Log message details
95
+ message way: WAY_POST,
96
+ exchange: id,
97
+ key: relay_url,
98
+ body: post_body
182
99
 
183
100
  # Push message to URL
184
- puts "> POST #{url}"
185
- response = RestClient.post url.to_s, body, :content_type => :json
186
- puts "< #{response.body}"
101
+ response = RestClient.post relay_url.to_s, JSON.pretty_generate(post_body), :content_type => :json
102
+ info "#{id}: #{response.body}"
187
103
 
188
104
  rescue Exception => e
189
105
  abort "propagate: #{e.message}"
@@ -220,49 +136,3 @@ module PushyDaemon
220
136
 
221
137
  end
222
138
 
223
-
224
- # def prepare_shout
225
-
226
-
227
- # # Prepare shout config
228
- # shout_config = config[:shout]
229
- # shout_exchange = nil
230
- # shout_keys = []
231
-
232
- # if shout_config.is_a? Hash
233
- # shout_exchange = topic(channel, shout_config[:topic])
234
- # shout_keys = shout_config[:keys] if shout_config[:keys].is_a? Array
235
- # end
236
-
237
- # end
238
-
239
- # def endlessly
240
- # # Endless loop with shout config
241
- # begin
242
- # loop do
243
- # if shout_exchange
244
- # random_string = SecureRandom.hex
245
- # random_key = shout_keys.sample || "random"
246
- # shout shout_exchange, [:ping, random_key, random_string], {}
247
- # end
248
- # sleep 1
249
- # end
250
- # rescue AMQ::Protocol::EmptyResponseError => e
251
- # abort "ERROR: AMQ::Protocol::EmptyResponseError (#{e.inspect})"
252
- # rescue Bunny::TCPConnectionFailedForAllHosts => e
253
- # abort "ERROR: cannot connect to RabbitMQ hosts (#{e.inspect})"
254
- # rescue Bunny::ChannelAlreadyClosed => e
255
- # abort "ERROR: channel unexpectedly closed (#{e.inspect})"
256
- # # sleep 1
257
- # # retry
258
- # rescue Bunny::PreconditionFailed => e
259
- # abort "ERROR: precondition failed (#{e.inspect})"
260
- # rescue Interrupt => e
261
- # channel.close
262
- # conn.close
263
- # abort "QUITTING"
264
- # end
265
- # end
266
-
267
- # Dump configuration
268
- # Hashie.symbolize_keys! config
@@ -0,0 +1,101 @@
1
+ require 'yaml'
2
+ require 'json'
3
+
4
+ module PushyDaemon
5
+
6
+ class ShouterResponseError < StandardError; end
7
+ class ShouterChannelClosed < StandardError; end
8
+ class ShouterPreconditionFailed < StandardError; end
9
+ class ShouterInterrupted < StandardError; end
10
+ class EndpointTopicContext < StandardError; end
11
+
12
+ class Shouter < Endpoint
13
+
14
+ attr_accessor :table
15
+
16
+ def initialize(logger)
17
+ # Init
18
+ @logger = logger
19
+ @keys = []
20
+
21
+ # Start connexion to RabbitMQ and create channel
22
+ conn = connect Config.bus
23
+ @channel = conn.create_channel
24
+ info "connected on a channel"
25
+
26
+ # Check config
27
+ config_shout = Config[:shout]
28
+ if (config_shout.is_a? Enumerable) && !config_shout.empty?
29
+ @keys = config_shout[:keys] if config_shout[:keys].is_a? Array
30
+ @topic = config_shout[:topic]
31
+
32
+ info "found topic: #{@topic}"
33
+ info "found keys: #{@keys.join(', ')}"
34
+ else
35
+ abort "prepare: empty [shout] section"
36
+ end
37
+
38
+ # Create exchange
39
+ raise PushyDaemon::EndpointTopicContext unless @topic
40
+ @exchange = @channel.topic(@topic, durable: true, persistent: true)
41
+
42
+ # if shout_config.is_a? Hash
43
+ # shout_keys = shout_config[:keys] if config_shout[:keys].is_a? Array
44
+ # end
45
+
46
+ rescue Bunny::TCPConnectionFailedForAllHosts => e
47
+ abort "ERROR: cannot connect to RabbitMQ hosts (#{e.inspect})"
48
+ end
49
+
50
+ def shout
51
+ # Prepare exchange
52
+ loop do
53
+ if true # shout_exchange
54
+ random_string = SecureRandom.hex
55
+ random_key = @keys.sample || "random"
56
+ channel_shout [:ping, random_key, random_string], {}
57
+ end
58
+ sleep 1
59
+ end
60
+ rescue AMQ::Protocol::EmptyResponseError => e
61
+ raise PushyDaemon::ShouterResponseError, "#{e.class} (#{e.inspect})"
62
+ rescue Bunny::ChannelAlreadyClosed => e
63
+ raise PushyDaemon::ShouterChannelClosed, "#{e.class} (#{e.inspect})"
64
+ rescue Bunny::PreconditionFailed => e
65
+ raise PushyDaemon::ShouterPreconditionFailed, "#{e.class} (#{e.inspect})"
66
+
67
+ rescue Interrupt => e
68
+ @channel.close
69
+ # conn.close
70
+ raise PushyDaemon::ShouterInterrupted, "#{e.class} (#{e.inspect})"
71
+ end
72
+
73
+ private
74
+
75
+ def channel_shout keys, body = {}
76
+ # Add timestamp
77
+ headers = {
78
+ sent_at: DateTime.now.iso8601,
79
+ sent_by: Config.name
80
+ }
81
+ exchange_name = @exchange.name
82
+
83
+ # Prepare key and data
84
+ routing_key = keys.unshift(exchange_name).join('.')
85
+
86
+ # Announce shout
87
+ message way: WAY_OUT, exchange: exchange_name, key: routing_key, body: nil, attrs: {}
88
+
89
+ # Publish
90
+ @exchange.publish(body.to_json,
91
+ routing_key: routing_key,
92
+ headers: headers,
93
+ app_id: Config.name,
94
+ content_type: "application/json",
95
+ )
96
+
97
+ end
98
+
99
+ end
100
+ end
101
+
data/lib/pushyd.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  # Global libs
3
2
  require "rubygems"
4
3
  require "json"
@@ -6,8 +5,11 @@ require "thread"
6
5
  require "singleton"
7
6
  # require "newrelic_rpm"
8
7
 
9
- # Project's libs
8
+ # Project libs
10
9
  require_relative "pushyd/config"
10
+ require_relative "pushyd/constants"
11
+ require_relative "pushyd/endpoint"
11
12
  require_relative "pushyd/proxy"
13
+ require_relative "pushyd/shouter"
12
14
  require_relative "pushyd/daemon"
13
15
 
data/pushyd.gemspec CHANGED
@@ -1,7 +1,7 @@
1
1
  # coding: utf-8
2
2
  Gem::Specification.new do |spec|
3
3
  # Project version
4
- spec.version = "0.0.2"
4
+ spec.version = "0.1.1"
5
5
 
6
6
  # Project description
7
7
  spec.name = "pushyd"
@@ -14,10 +14,10 @@ Gem::Specification.new do |spec|
14
14
  spec.date = Time.now.strftime("%Y-%m-%d")
15
15
 
16
16
  # List files and executables
17
- spec.files = `git ls-files -z`.split("\x0").reject{ |f| f == "dashboard.png"}
17
+ spec.files = `git ls-files -z`.split("\x0")
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
- spec.required_ruby_version = ">= 2.2"
20
+ spec.required_ruby_version = ">= 2.1"
21
21
 
22
22
  # Development dependencies
23
23
  spec.add_development_dependency "bundler", "~> 1.6"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pushyd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno MEDICI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-02 00:00:00.000000000 Z
11
+ date: 2016-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -180,8 +180,11 @@ files:
180
180
  - defaults.yml
181
181
  - lib/pushyd.rb
182
182
  - lib/pushyd/config.rb
183
+ - lib/pushyd/constants.rb
183
184
  - lib/pushyd/daemon.rb
185
+ - lib/pushyd/endpoint.rb
184
186
  - lib/pushyd/proxy.rb
187
+ - lib/pushyd/shouter.rb
185
188
  - pushyd.gemspec
186
189
  homepage: http://github.com/bmedici/pushyd
187
190
  licenses:
@@ -195,7 +198,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
195
198
  requirements:
196
199
  - - ">="
197
200
  - !ruby/object:Gem::Version
198
- version: '2.2'
201
+ version: '2.1'
199
202
  required_rubygems_version: !ruby/object:Gem::Requirement
200
203
  requirements:
201
204
  - - ">="