pushyd 0.0.2 → 0.1.1

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.
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
  - - ">="