pipeline_toolkit 1.1.0 → 1.2.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.
- 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
|
+
|