pipeline_toolkit 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.markdown +1 -1
- data/README.markdown +38 -19
- data/bin/msg_generator +3 -4
- data/bin/msg_push +3 -4
- data/bin/msg_sink +3 -7
- data/bin/msg_subscribe +3 -4
- data/lib/pipeline_toolkit.rb +5 -5
- data/lib/pipeline_toolkit/amqp/abstract.rb +81 -78
- data/lib/pipeline_toolkit/amqp/reader.rb +55 -52
- data/lib/pipeline_toolkit/amqp/writer.rb +42 -39
- data/lib/pipeline_toolkit/{commands/msg_generator/cli.rb → cli/msg_generator_cli.rb} +6 -5
- data/lib/pipeline_toolkit/{commands/msg_push/cli.rb → cli/msg_push_cli.rb} +16 -8
- data/lib/pipeline_toolkit/cli/msg_sink_cli.rb +28 -0
- data/lib/pipeline_toolkit/{commands/msg_subscribe/cli.rb → cli/msg_subscribe_cli.rb} +23 -14
- data/lib/pipeline_toolkit/cucumber.rb +5 -3
- data/lib/pipeline_toolkit/cucumber/amqp.rb +1 -1
- data/lib/pipeline_toolkit/cucumber/in_out_process.rb +50 -0
- data/lib/pipeline_toolkit/cucumber/machine.rb +44 -26
- data/lib/pipeline_toolkit/cucumber/machine_steps.rb +15 -9
- data/lib/pipeline_toolkit/cucumber/{msg_sub_machine.rb → msg_sub_process.rb} +5 -5
- data/lib/pipeline_toolkit/handlers/message_handler.rb +43 -39
- data/lib/pipeline_toolkit/message_coder.rb +38 -25
- data/lib/pipeline_toolkit/message_command.rb +167 -157
- data/lib/pipeline_toolkit/message_generator.rb +22 -18
- data/lib/pipeline_toolkit/message_pusher.rb +46 -47
- data/lib/pipeline_toolkit/message_sink.rb +9 -5
- data/lib/pipeline_toolkit/message_subscriber.rb +190 -183
- data/lib/pipeline_toolkit/monitoring/monitor_server.rb +64 -109
- data/lib/pipeline_toolkit/util/hash_ext.rb +8 -0
- data/lib/pipeline_toolkit/util/indifferent_hash.rb +10 -0
- data/lib/pipeline_toolkit/{socket_util.rb → util/socket_util.rb} +1 -1
- metadata +25 -115
- data/lib/pipeline_toolkit/commands/msg_sink/cli.rb +0 -41
- data/lib/pipeline_toolkit/open_hash.rb +0 -24
@@ -1,53 +1,56 @@
|
|
1
1
|
require 'pipeline_toolkit/amqp/abstract'
|
2
2
|
require 'pipeline_toolkit/message_coder'
|
3
3
|
|
4
|
-
module
|
4
|
+
module PipelineToolkit
|
5
|
+
module Amqp # :nodoc:
|
5
6
|
|
6
|
-
##
|
7
|
-
# The Writer provides functionality specific to asynchronous message
|
8
|
-
# publishing to the AMQP server.
|
9
|
-
#
|
10
|
-
module Writer
|
11
|
-
include Amqp::Abstract
|
12
|
-
|
13
7
|
##
|
14
|
-
#
|
15
|
-
#
|
8
|
+
# The Writer provides functionality specific to asynchronous message
|
9
|
+
# publishing to the AMQP server.
|
16
10
|
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
module Writer
|
12
|
+
include Amqp::Abstract
|
13
|
+
|
14
|
+
##
|
15
|
+
# Initialize the AMQP server specific to handling
|
16
|
+
# of messages publishing to the server.
|
17
|
+
#
|
18
|
+
def initialize_writer
|
19
|
+
DefaultLogger.debug("Amqp::Writer#initialize_writer") if options[:env] == "development"
|
20
|
+
initialize_connection
|
21
|
+
initialize_channel
|
22
|
+
initialize_exchange
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
##
|
26
|
+
# Initializes the queues to publish the messages too.
|
27
|
+
#
|
28
|
+
def initialize_queues
|
29
|
+
DefaultLogger.debug("Amqp::Writer#initialize_queues") if options[:env] == "development"
|
30
|
+
options[:queues].each do |name, routing_key|
|
31
|
+
initialize_queue(name)
|
32
|
+
bind_queue(routing_key)
|
33
|
+
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
##
|
37
|
+
# Handles the publishing of messages into the AMQP server.
|
38
|
+
#
|
39
|
+
# @param message<Hash> The message to publish
|
40
|
+
#
|
41
|
+
def publish(message)
|
42
|
+
DefaultLogger.debug("Amqp::Writer#publish(message)") if options[:env] == "development"
|
43
|
+
if message.has_key?(:routing_key)
|
44
|
+
@exchange.publish(MessageCoder.encode(message), :key => message[:routing_key])
|
45
|
+
else
|
46
|
+
@exchange.publish(MessageCoder.encode(message))
|
47
|
+
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
50
|
+
def queue_names
|
51
|
+
options[:queues].map { |name, type| name }
|
52
|
+
end
|
53
|
+
|
51
54
|
end
|
52
55
|
|
53
56
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'pp'
|
2
1
|
require 'trollop'
|
3
2
|
require 'eventmachine'
|
4
3
|
require 'pipeline_toolkit'
|
@@ -14,10 +13,11 @@ Signal.trap('TERM') do
|
|
14
13
|
exit(0)
|
15
14
|
# TODO: complete signal trap terminate
|
16
15
|
end
|
17
|
-
|
18
|
-
module
|
19
|
-
module
|
20
|
-
|
16
|
+
|
17
|
+
module PipelineToolkit # :nodoc:
|
18
|
+
module CLI
|
19
|
+
|
20
|
+
class MsgGeneratorCLI
|
21
21
|
|
22
22
|
def self.execute(stdout, arguments=[])
|
23
23
|
|
@@ -44,5 +44,6 @@ Options:
|
|
44
44
|
MessageGenerator.new(opts).start
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
47
48
|
end
|
48
49
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
require 'pp'
|
2
1
|
require 'trollop'
|
3
2
|
require 'eventmachine'
|
3
|
+
require 'pipeline_toolkit'
|
4
4
|
|
5
|
-
module
|
6
|
-
module
|
7
|
-
class
|
5
|
+
module PipelineToolkit # :nodoc:
|
6
|
+
module CLI
|
7
|
+
class MsgPushCLI
|
8
8
|
def self.execute(stdout, arguments=[])
|
9
9
|
|
10
10
|
opts = Trollop::options do
|
@@ -25,22 +25,30 @@ Examples:
|
|
25
25
|
Options:
|
26
26
|
EOL
|
27
27
|
|
28
|
-
|
28
|
+
banner <<-EOL
|
29
|
+
|
30
|
+
Queue/exchange:
|
31
|
+
EOL
|
29
32
|
opt :exchange, "Exchange name", :type => :string, :required => true, :short => :x
|
30
33
|
opt :type, "Exchange type (direct, fanout, topic)", :default => "fanout", :short => :t
|
31
34
|
opt :passive, "If true, the exchange will not be created if it does not already exist.", :default => false, :short => :s
|
32
35
|
opt :durable, "If true, the exchange will be marked as durable.", :default => false, :short => :d
|
33
|
-
|
34
|
-
# Msg queues
|
35
36
|
opt :queues, "Destination queue(s) (e.g. queue1:routing_key, queue2:routing_key). Routing keys will be ignored if exchange type is not topic.", :type => :strings, :required => false, :short => :q
|
36
37
|
|
37
|
-
|
38
|
+
banner <<-EOL
|
39
|
+
|
40
|
+
AMQP server:
|
41
|
+
EOL
|
38
42
|
opt :host, "AMQP message server host", :default => "localhost", :short => :h
|
39
43
|
opt :port, "AMQP message server port", :default => 5672, :short => :p
|
40
44
|
opt :user, "AMQP message server username", :default => "guest", :short => :u
|
41
45
|
opt :pass, "AMQP message server password", :default => "guest", :short => :w
|
42
46
|
opt :vhost, "AMQP message server vhost", :default => "/", :short => :v
|
43
47
|
|
48
|
+
banner <<-EOL
|
49
|
+
|
50
|
+
Misc:
|
51
|
+
EOL
|
44
52
|
opt :env, "Environment (development, production)", :default => "development", :short => :e
|
45
53
|
end
|
46
54
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'trollop'
|
2
|
+
require 'pipeline_toolkit'
|
3
|
+
|
4
|
+
module PipelineToolkit # :nodoc:
|
5
|
+
module CLI
|
6
|
+
|
7
|
+
class MsgSinkCLI
|
8
|
+
def self.execute(stdout, arguments=[])
|
9
|
+
|
10
|
+
opts = Trollop::options do
|
11
|
+
banner <<-EOL
|
12
|
+
Message Sink
|
13
|
+
------------
|
14
|
+
Swallows messages but still acknowledges them (like a /dev/null, but with acks)
|
15
|
+
|
16
|
+
Usage:
|
17
|
+
msg_sink
|
18
|
+
EOL
|
19
|
+
|
20
|
+
opt :env, "The environment to run (development, production)", :default => "development", :short => :e
|
21
|
+
end
|
22
|
+
|
23
|
+
MessageSink.new(opts).start
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'trollop'
|
2
2
|
require 'eventmachine'
|
3
|
+
require 'pipeline_toolkit'
|
3
4
|
|
4
|
-
module
|
5
|
-
module
|
5
|
+
module PipelineToolkit # :nodoc:
|
6
|
+
module CLI # :nodoc:
|
6
7
|
|
7
|
-
class
|
8
|
+
class MsgSubscribeCLI
|
8
9
|
def self.execute(stdout, arguments=[])
|
9
10
|
|
10
11
|
opts = Trollop::options do
|
@@ -23,31 +24,39 @@ Examples:
|
|
23
24
|
--host www.domain.com --port 7000
|
24
25
|
|
25
26
|
Options:
|
26
|
-
|
27
|
+
EOL
|
27
28
|
|
28
|
-
|
29
|
+
banner <<-EOL
|
30
|
+
|
31
|
+
Queue/exchange:
|
32
|
+
EOL
|
29
33
|
opt :exchange, "The exchange name", :type => :string, :required => true, :short => :x
|
30
34
|
opt :type, "The exchange type (direct, fanout or topic)", :default => "fanout", :short => :t
|
31
35
|
opt :passive, "If set to true, the server will not create the exchange if it does not already exist.", :default => false, :short => :s
|
32
36
|
opt :durable, "If set to true, the exchange will be marked as durable.", :default => false, :short => :d
|
33
|
-
|
34
|
-
# Messages
|
35
37
|
opt :queue, "The destination queue (queue:routing_key). Routing keys will be ignored if exchange type is not topic.", :short => :q, :type => :string, :required => true
|
36
|
-
opt :
|
38
|
+
opt :no_acks, "If flag is set then the server does not use acknowledgments for messages.", :type => :flag
|
39
|
+
|
40
|
+
banner <<-EOL
|
37
41
|
|
38
|
-
|
42
|
+
AMQP server:
|
43
|
+
EOL
|
39
44
|
opt :host, "The AMQP message server host", :default => "localhost", :short => :h
|
40
45
|
opt :port, "The AMQP message server port", :default => 5672, :short => :p
|
41
46
|
opt :user, "The AMQP message server username", :default => "guest", :short => :u
|
42
47
|
opt :pass, "The AMQP message server username", :default => "guest", :short => :w
|
43
48
|
opt :vhost, "The AMQP message server vhost", :default => "/", :short => :v
|
44
49
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
opt :
|
50
|
+
banner <<-EOL
|
51
|
+
|
52
|
+
Monitoring:
|
53
|
+
EOL
|
54
|
+
opt :http_port, "The port the HTTP monitoring interface runs on. A value must be given to enable the monitoring interface - by default the monitoring interface isn't enabled. An HTML or JSON response can be obtained by suffixing either .html or .json to the request", :type => :integer, :short => :o
|
50
55
|
|
56
|
+
banner <<-EOL
|
57
|
+
|
58
|
+
Misc:
|
59
|
+
EOL
|
51
60
|
opt :env, "The environment to run (development, production)", :default => "development", :short => :e
|
52
61
|
end
|
53
62
|
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
require 'pipeline_toolkit/open_hash'
|
2
2
|
require 'pipeline_toolkit/cucumber/amqp'
|
3
|
-
require 'pipeline_toolkit/cucumber/
|
3
|
+
require 'pipeline_toolkit/cucumber/in_out_process'
|
4
4
|
require 'pipeline_toolkit/cucumber/machine'
|
5
|
+
require 'pipeline_toolkit/cucumber/msg_sub_process'
|
6
|
+
|
5
7
|
require 'pipeline_toolkit/cucumber/machine_steps'
|
6
|
-
require 'pipeline_toolkit/cucumber/
|
8
|
+
require 'pipeline_toolkit/cucumber/amqp_steps'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'pipeline_toolkit'
|
2
|
+
require 'background_process'
|
3
|
+
|
4
|
+
module PipelineToolkit
|
5
|
+
module Cucumber
|
6
|
+
|
7
|
+
# Provides an interface for running and interacting with a stdin/stdout process which
|
8
|
+
# talks messages.
|
9
|
+
class InOutProcess
|
10
|
+
|
11
|
+
def run(command)
|
12
|
+
@process = BackgroundProcess.run("ruby -rubygems bin/#{command}")
|
13
|
+
# wait for process to start
|
14
|
+
sleep(0.5)
|
15
|
+
raise @process.stderr.gets unless @process.running?
|
16
|
+
end
|
17
|
+
|
18
|
+
def kill
|
19
|
+
@process.kill
|
20
|
+
@process.wait
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_messages(number, timeout = 5)
|
24
|
+
output_msgs = []
|
25
|
+
get_lines(@process.stdout, number, timeout) do |line|
|
26
|
+
msg = MessageCoder.decode(line)
|
27
|
+
output_msgs << msg
|
28
|
+
end
|
29
|
+
output_msgs
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def get_lines(io, number, timeout = 5)
|
35
|
+
count = 0
|
36
|
+
Timeout::timeout(timeout) do
|
37
|
+
while (count < number)
|
38
|
+
line = io.gets
|
39
|
+
raise "The machine quit before I received all the messages!!" if line.nil?
|
40
|
+
yield line if block_given?
|
41
|
+
count += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue Timeout::Error
|
45
|
+
raise "Timed out waiting for messages. Received #{count} of #{number}"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -4,53 +4,71 @@ require 'background_process'
|
|
4
4
|
module PipelineToolkit
|
5
5
|
module Cucumber
|
6
6
|
|
7
|
-
# Provides an interface for running and interacting with a machine
|
8
|
-
class Machine
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
# Provides an interface for running and interacting with a pipeline_toolkit machine.
|
8
|
+
class Machine < InOutProcess
|
9
|
+
|
10
|
+
attr_reader :use_acknowledgements
|
11
|
+
|
12
|
+
def run(command, use_acknowledgements = true)
|
13
|
+
super(command)
|
14
|
+
|
15
|
+
@use_acknowledgements = use_acknowledgements
|
16
|
+
setup_system
|
15
17
|
end
|
16
18
|
|
17
19
|
def kill
|
18
|
-
|
19
|
-
|
20
|
+
destroy_sys_pipe
|
21
|
+
super
|
20
22
|
end
|
21
23
|
|
22
24
|
def input_messages(messages)
|
23
25
|
messages.each do |message|
|
24
|
-
|
25
|
-
message.default = nil
|
26
|
+
store_acknowledgement(message) if use_acknowledgements
|
26
27
|
@process.stdin.puts(MessageCoder.encode(message))
|
27
28
|
@process.stdin.flush
|
28
29
|
end
|
29
30
|
end
|
30
|
-
|
31
|
-
def
|
31
|
+
|
32
|
+
def get_acknowledged_messages(number, timeout = 5)
|
32
33
|
output_msgs = []
|
33
|
-
|
34
|
+
get_lines(@sys_pipe, number, timeout) do |line|
|
34
35
|
msg = MessageCoder.decode(line)
|
35
36
|
output_msgs << msg
|
36
37
|
end
|
37
38
|
output_msgs
|
39
|
+
@messages_to_ack.values_at( *output_msgs.map { |msg| msg[:ack_id] } )
|
38
40
|
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
44
|
+
def setup_system
|
45
|
+
create_sys_pipe
|
46
|
+
message = { :msg_type => "system",
|
47
|
+
:sys_pipe => @sys_pipe.path,
|
48
|
+
:ack => use_acknowledgements }
|
49
|
+
|
50
|
+
# send sys_message
|
51
|
+
input_messages([message])
|
52
|
+
|
53
|
+
# get pipe_desc off sys_pipe
|
54
|
+
get_lines(@sys_pipe, 1, 5)
|
53
55
|
end
|
56
|
+
|
57
|
+
def destroy_sys_pipe
|
58
|
+
`rm #{@sys_pipe.path}`
|
59
|
+
end
|
60
|
+
|
61
|
+
def create_sys_pipe
|
62
|
+
name = File.join("/tmp", "test_sys_pipe_#{Time.now.to_i}_#{rand(9999)}")
|
63
|
+
`mkfifo #{name}`
|
64
|
+
@sys_pipe = File.new(name, "r+")
|
65
|
+
end
|
66
|
+
|
67
|
+
def store_acknowledgement(message)
|
68
|
+
@messages_to_ack ||= {}
|
69
|
+
message[:ack_id] = "#{Time.now.to_i}_#{rand(9999)}"
|
70
|
+
@messages_to_ack[message[:ack_id]] = message
|
71
|
+
end
|
54
72
|
|
55
73
|
end
|
56
74
|
end
|
@@ -1,23 +1,29 @@
|
|
1
|
-
# ---------
|
2
|
-
# Machine lifecycle
|
3
1
|
|
4
2
|
Given /^I run this machine:$/ do |command|
|
5
|
-
@
|
6
|
-
@
|
3
|
+
@process = PipelineToolkit::Cucumber::Machine.new
|
4
|
+
@process.run(command)
|
7
5
|
end
|
8
6
|
|
9
7
|
# Clean up backround processes after each scenario
|
10
8
|
After do
|
11
|
-
@
|
9
|
+
@process.kill unless @process.nil?
|
12
10
|
end
|
13
11
|
|
14
12
|
When /^I input these messages:$/ do |messages|
|
15
|
-
@
|
13
|
+
@process.input_messages(messages.hashes)
|
16
14
|
end
|
17
15
|
|
18
|
-
Then /^these messages are
|
19
|
-
received_messages = @
|
16
|
+
Then /^these messages are passed on:$/ do |expected_msgs|
|
17
|
+
received_messages = @process.get_messages(expected_msgs.rows.size)
|
20
18
|
# turn everything into str
|
21
19
|
received_messages.map! { |h| h.each { |k,v| h[k] = v.to_s }}
|
22
20
|
expected_msgs.diff!(received_messages)
|
23
|
-
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^these messages are acknowledged:$/ do |expected_msgs|
|
24
|
+
received_messages = @process.get_acknowledged_messages(expected_msgs.rows.size)
|
25
|
+
# turn everything into str
|
26
|
+
received_messages.map! { |h| h.each { |k,v| h[k] = v.to_s }}
|
27
|
+
expected_msgs.diff!(received_messages)
|
28
|
+
end
|
29
|
+
|