pantry 0.0.0 → 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.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +15 -0
- data/Guardfile +16 -0
- data/LICENSE +20 -0
- data/README.md +53 -0
- data/Rakefile +18 -0
- data/Vagrantfile +86 -0
- data/bin/pantry +11 -0
- data/bin/pantry-client +38 -0
- data/bin/pantry-server +33 -0
- data/dist/client.yml +79 -0
- data/dist/server.yml +56 -0
- data/dist/upstart/pantry-client.conf +12 -0
- data/dist/upstart/pantry-server.conf +12 -0
- data/doc/message_packet.dot +19 -0
- data/doc/message_packet.dot.png +0 -0
- data/doc/network_topology.dot +42 -0
- data/doc/network_topology.dot.png +0 -0
- data/lib/celluloid_zmq_patches.rb +16 -0
- data/lib/opt_parse_plus.rb +184 -0
- data/lib/pantry.rb +197 -0
- data/lib/pantry/cli.rb +154 -0
- data/lib/pantry/client.rb +131 -0
- data/lib/pantry/client_info.rb +34 -0
- data/lib/pantry/client_registry.rb +104 -0
- data/lib/pantry/command.rb +194 -0
- data/lib/pantry/command_handler.rb +53 -0
- data/lib/pantry/command_line.rb +115 -0
- data/lib/pantry/commands/create_client.rb +30 -0
- data/lib/pantry/commands/download_directory.rb +35 -0
- data/lib/pantry/commands/echo.rb +32 -0
- data/lib/pantry/commands/edit_application.rb +60 -0
- data/lib/pantry/commands/register_client.rb +38 -0
- data/lib/pantry/commands/status.rb +78 -0
- data/lib/pantry/commands/sync_directory.rb +50 -0
- data/lib/pantry/commands/update_application.rb +45 -0
- data/lib/pantry/commands/upload_file.rb +68 -0
- data/lib/pantry/communication.rb +20 -0
- data/lib/pantry/communication/client.rb +75 -0
- data/lib/pantry/communication/client_filter.rb +117 -0
- data/lib/pantry/communication/file_service.rb +125 -0
- data/lib/pantry/communication/file_service/file_progress.rb +164 -0
- data/lib/pantry/communication/file_service/receive_file.rb +97 -0
- data/lib/pantry/communication/file_service/send_file.rb +74 -0
- data/lib/pantry/communication/publish_socket.rb +20 -0
- data/lib/pantry/communication/reading_socket.rb +89 -0
- data/lib/pantry/communication/receive_socket.rb +23 -0
- data/lib/pantry/communication/security.rb +44 -0
- data/lib/pantry/communication/security/authentication.rb +98 -0
- data/lib/pantry/communication/security/curve_key_store.rb +120 -0
- data/lib/pantry/communication/security/curve_security.rb +70 -0
- data/lib/pantry/communication/security/null_security.rb +32 -0
- data/lib/pantry/communication/send_socket.rb +19 -0
- data/lib/pantry/communication/serialize_message.rb +84 -0
- data/lib/pantry/communication/server.rb +97 -0
- data/lib/pantry/communication/subscribe_socket.rb +33 -0
- data/lib/pantry/communication/wait_list.rb +45 -0
- data/lib/pantry/communication/writing_socket.rb +46 -0
- data/lib/pantry/config.rb +182 -0
- data/lib/pantry/file_editor.rb +67 -0
- data/lib/pantry/logger.rb +78 -0
- data/lib/pantry/message.rb +134 -0
- data/lib/pantry/multi_command.rb +36 -0
- data/lib/pantry/server.rb +132 -0
- data/lib/pantry/test/acceptance.rb +83 -0
- data/lib/pantry/test/support/fake_fs.rb +31 -0
- data/lib/pantry/test/support/matchers.rb +13 -0
- data/lib/pantry/test/support/minitest.rb +13 -0
- data/lib/pantry/test/support/mock_ui.rb +23 -0
- data/lib/pantry/test/unit.rb +13 -0
- data/lib/pantry/ui.rb +68 -0
- data/lib/pantry/version.rb +3 -0
- data/pantry.gemspec +40 -0
- data/test/acceptance/cli/error_handling_test.rb +7 -0
- data/test/acceptance/cli/execute_command_on_clients_test.rb +32 -0
- data/test/acceptance/cli/request_info_from_server_test.rb +44 -0
- data/test/acceptance/communication/client_requests_info_from_server_test.rb +28 -0
- data/test/acceptance/communication/heartbeat_test.rb +19 -0
- data/test/acceptance/communication/pub_sub_communication_test.rb +53 -0
- data/test/acceptance/communication/security_test.rb +117 -0
- data/test/acceptance/communication/server_requests_info_from_client_test.rb +41 -0
- data/test/acceptance/test_helper.rb +25 -0
- data/test/fixtures/config.yml +22 -0
- data/test/fixtures/empty.yml +2 -0
- data/test/fixtures/file_to_upload +3 -0
- data/test/root_dir/.gitkeep +0 -0
- data/test/unit/cli_test.rb +173 -0
- data/test/unit/client_registry_test.rb +61 -0
- data/test/unit/client_test.rb +128 -0
- data/test/unit/command_handler_test.rb +79 -0
- data/test/unit/command_line_test.rb +5 -0
- data/test/unit/command_test.rb +206 -0
- data/test/unit/commands/create_client_test.rb +25 -0
- data/test/unit/commands/download_directory_test.rb +58 -0
- data/test/unit/commands/echo_test.rb +22 -0
- data/test/unit/commands/edit_application_test.rb +84 -0
- data/test/unit/commands/register_client_test.rb +41 -0
- data/test/unit/commands/status_test.rb +81 -0
- data/test/unit/commands/sync_directory_test.rb +75 -0
- data/test/unit/commands/update_application_test.rb +35 -0
- data/test/unit/commands/upload_file_test.rb +51 -0
- data/test/unit/communication/client_filter_test.rb +262 -0
- data/test/unit/communication/client_test.rb +99 -0
- data/test/unit/communication/file_service/receive_file_test.rb +214 -0
- data/test/unit/communication/file_service/send_file_test.rb +110 -0
- data/test/unit/communication/file_service_test.rb +56 -0
- data/test/unit/communication/publish_socket_test.rb +19 -0
- data/test/unit/communication/reading_socket_test.rb +110 -0
- data/test/unit/communication/receive_socket_test.rb +20 -0
- data/test/unit/communication/security/authentication_test.rb +97 -0
- data/test/unit/communication/security/curve_key_store_test.rb +110 -0
- data/test/unit/communication/security/curve_security_test.rb +44 -0
- data/test/unit/communication/security/null_security_test.rb +15 -0
- data/test/unit/communication/security_test.rb +49 -0
- data/test/unit/communication/send_socket_test.rb +19 -0
- data/test/unit/communication/serialize_message_test.rb +128 -0
- data/test/unit/communication/server_test.rb +106 -0
- data/test/unit/communication/subscribe_socket_test.rb +46 -0
- data/test/unit/communication/wait_list_test.rb +60 -0
- data/test/unit/communication/writing_socket_test.rb +46 -0
- data/test/unit/config_test.rb +150 -0
- data/test/unit/logger_test.rb +79 -0
- data/test/unit/message_test.rb +179 -0
- data/test/unit/multi_command_test.rb +45 -0
- data/test/unit/opt_parse_plus_test.rb +218 -0
- data/test/unit/pantry_test.rb +82 -0
- data/test/unit/server_test.rb +166 -0
- data/test/unit/test_helper.rb +25 -0
- data/test/unit/ui_test.rb +58 -0
- metadata +389 -13
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# Access Pantry's logger.
|
|
4
|
+
def self.logger
|
|
5
|
+
@@logger ||= Pantry::Logger.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.logger=(logger)
|
|
9
|
+
@@logger = logger
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Wrapper around the Celluloid's logging system. Depending on the passed in
|
|
13
|
+
# config, will send to STDOUT, Syslog, or a given file.
|
|
14
|
+
# See Celluloid::Logger for API (should be the same as Ruby's Logger API)
|
|
15
|
+
class Logger
|
|
16
|
+
|
|
17
|
+
def initialize(config = Pantry.config)
|
|
18
|
+
logger =
|
|
19
|
+
if config.log_to.nil? || config.log_to == "stdout"
|
|
20
|
+
::Logger.new(STDOUT)
|
|
21
|
+
elsif config.log_to == "syslog"
|
|
22
|
+
::Syslog::Logger.new(config.syslog_program_name)
|
|
23
|
+
else
|
|
24
|
+
::Logger.new(config.log_to)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
logger.level = log_level(config.log_level)
|
|
28
|
+
Celluloid.logger = logger
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Turn off all logging entirely
|
|
32
|
+
def disable!
|
|
33
|
+
Celluloid.logger = NullLogger.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Forward all methods on to the internal Celluloid Logger.
|
|
37
|
+
def method_missing(*args)
|
|
38
|
+
Celluloid.logger.send(*args) if Celluloid.logger
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
protected
|
|
42
|
+
|
|
43
|
+
def log_level(log_level_string)
|
|
44
|
+
case log_level_string.to_s
|
|
45
|
+
when "debug"
|
|
46
|
+
::Logger::DEBUG
|
|
47
|
+
when "info"
|
|
48
|
+
::Logger::INFO
|
|
49
|
+
when "warn"
|
|
50
|
+
::Logger::WARN
|
|
51
|
+
when "error"
|
|
52
|
+
::Logger::ERROR
|
|
53
|
+
when "fatal"
|
|
54
|
+
::Logger::FATAL
|
|
55
|
+
else
|
|
56
|
+
raise "Unknown log level given: #{log_level_string}"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Because Celluloid tries to log things on shut-down that throw
|
|
63
|
+
# tons of exceptions if the logger is nil
|
|
64
|
+
class NullLogger
|
|
65
|
+
def debug(*args)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def info(*args)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def warn(*args)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def error(*args)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# A Message is the container for all network communication between Clients and Servers.
|
|
4
|
+
# Messages know what stream they've been sent down, have a type to differentiate them
|
|
5
|
+
# from each other, and an arbitrarily large body.
|
|
6
|
+
#
|
|
7
|
+
# Every message has three sections, the stream, metadata, and body. The stream defines
|
|
8
|
+
# where the message needs to go. The metadata defines information about the message, its
|
|
9
|
+
# type, if it needs a response, and anything else that doesn't go in the body.
|
|
10
|
+
# The body is the request message itself and can be one or many parts.
|
|
11
|
+
class Message
|
|
12
|
+
|
|
13
|
+
# Unique identifier for this Message. Automatically generated
|
|
14
|
+
attr_reader :uuid
|
|
15
|
+
|
|
16
|
+
# Where or who is this message intended for (Can be an identity or a stream)
|
|
17
|
+
# Defaults to the catch-all stream `""`
|
|
18
|
+
attr_accessor :to
|
|
19
|
+
|
|
20
|
+
# Who is this message coming from (Should be an identity)
|
|
21
|
+
attr_accessor :from
|
|
22
|
+
|
|
23
|
+
# What type of message are we?
|
|
24
|
+
attr_accessor :type
|
|
25
|
+
|
|
26
|
+
# The full, raw body of the message.
|
|
27
|
+
attr_accessor :body
|
|
28
|
+
|
|
29
|
+
attr_accessor :custom_metadata
|
|
30
|
+
|
|
31
|
+
attr_writer :requires_response
|
|
32
|
+
|
|
33
|
+
def initialize(message_type = nil)
|
|
34
|
+
@type = message_type
|
|
35
|
+
@body = []
|
|
36
|
+
@to = ""
|
|
37
|
+
|
|
38
|
+
@requires_response = false
|
|
39
|
+
@forwarded = false
|
|
40
|
+
|
|
41
|
+
@custom_metadata = {}
|
|
42
|
+
|
|
43
|
+
@uuid = SecureRandom.uuid
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set the source of this message either by an object that responds to #identity
|
|
47
|
+
# or a string.
|
|
48
|
+
def from=(source)
|
|
49
|
+
if source.respond_to?(:identity)
|
|
50
|
+
@from = source.identity
|
|
51
|
+
else
|
|
52
|
+
@from = source
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def from_server?
|
|
57
|
+
@from == Pantry::SERVER_IDENTITY
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Flag this message as requiring a response
|
|
61
|
+
def requires_response!
|
|
62
|
+
@requires_response = true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Does this message require a response message?
|
|
66
|
+
def requires_response?
|
|
67
|
+
@requires_response
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Has this message been forwarded through the Server?
|
|
71
|
+
# This flag is checked when the message comes back through the Server,
|
|
72
|
+
# which lets it know if the message needs to continue back to another Client.
|
|
73
|
+
def forwarded!
|
|
74
|
+
@forwarded = true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def forwarded?
|
|
78
|
+
@forwarded
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Set custom metadata on this message.
|
|
82
|
+
def []=(key, val)
|
|
83
|
+
@custom_metadata[key] = val
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Access value from the custom metadata
|
|
87
|
+
def [](key)
|
|
88
|
+
@custom_metadata[key]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Build a copy of this message to use when responding
|
|
92
|
+
# to the message
|
|
93
|
+
def build_response
|
|
94
|
+
response = self.clone
|
|
95
|
+
response.body = []
|
|
96
|
+
response.to = self.from
|
|
97
|
+
response.from = self.to
|
|
98
|
+
response.requires_response = false
|
|
99
|
+
response.custom_metadata = self.custom_metadata.clone
|
|
100
|
+
response
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Add a message part to this Message's body
|
|
104
|
+
def <<(part)
|
|
105
|
+
@body << part
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Return all of this message's metadata as a hash
|
|
109
|
+
def metadata
|
|
110
|
+
{
|
|
111
|
+
:uuid => self.uuid,
|
|
112
|
+
:type => self.type,
|
|
113
|
+
:from => self.from,
|
|
114
|
+
:to => self.to || "",
|
|
115
|
+
:requires_response => self.requires_response?,
|
|
116
|
+
:forwarded => self.forwarded?,
|
|
117
|
+
:custom => @custom_metadata
|
|
118
|
+
}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Given a hash, pull out the parts into local variables
|
|
122
|
+
def metadata=(hash)
|
|
123
|
+
@uuid = hash[:uuid]
|
|
124
|
+
@type = hash[:type]
|
|
125
|
+
@from = hash[:from]
|
|
126
|
+
@to = hash[:to] || ""
|
|
127
|
+
@requires_response = hash[:requires_response]
|
|
128
|
+
@forwarded = hash[:forwarded]
|
|
129
|
+
@custom_metadata = hash[:custom]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# A MultiCommand allows specifying multiple Commands to be run in succession.
|
|
4
|
+
# Each command class given in .performs will have it's #perform executed and
|
|
5
|
+
# the return values will be grouped together in a single return Message.
|
|
6
|
+
#
|
|
7
|
+
# It's currently expected that each Command executed is done when it's #perform
|
|
8
|
+
# returns.
|
|
9
|
+
class MultiCommand < Command
|
|
10
|
+
|
|
11
|
+
# MultiCommand.performs takes a list of Command class constants.
|
|
12
|
+
def self.performs(command_classes = [])
|
|
13
|
+
@command_classes = command_classes
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.command_classes
|
|
17
|
+
@command_classes
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Iterate through each Command class and run that Command with the
|
|
21
|
+
# given message. The results of each Command will be combined into a single
|
|
22
|
+
# array return value and thus a single response Message back to the requester.
|
|
23
|
+
def perform(message)
|
|
24
|
+
Pantry.logger.debug("[#{client.identity}] Running MultiCommands")
|
|
25
|
+
|
|
26
|
+
self.class.command_classes.map do |command_class|
|
|
27
|
+
Pantry.logger.debug("[#{client.identity}] Running #{command_class}")
|
|
28
|
+
command = command_class.new
|
|
29
|
+
command.server_or_client = server_or_client
|
|
30
|
+
command.perform(message)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# The Pantry Server.
|
|
4
|
+
class Server
|
|
5
|
+
include Celluloid
|
|
6
|
+
finalizer :shutdown
|
|
7
|
+
|
|
8
|
+
attr_accessor :identity
|
|
9
|
+
|
|
10
|
+
attr_reader :client_registry
|
|
11
|
+
|
|
12
|
+
def initialize(network_stack_class = Communication::Server)
|
|
13
|
+
@commands = CommandHandler.new(self, Pantry.server_commands)
|
|
14
|
+
@identity = current_hostname
|
|
15
|
+
@clients = []
|
|
16
|
+
|
|
17
|
+
@client_registry = ClientRegistry.new
|
|
18
|
+
|
|
19
|
+
@networking = network_stack_class.new_link(self)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Start up the networking stack and start the server
|
|
23
|
+
def run
|
|
24
|
+
Pantry.set_proc_title("pantry server #{Pantry::VERSION}")
|
|
25
|
+
@networking.run
|
|
26
|
+
Pantry.logger.info("[#{@identity}] Server running")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Close down networking and clean up resources
|
|
30
|
+
def shutdown
|
|
31
|
+
Pantry.logger.info("[#{@identity}] Server Shutting Down")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Mark an authenticated client as checked-in
|
|
35
|
+
def register_client(client)
|
|
36
|
+
Pantry.logger.info("[#{@identity}] Received client registration :: #{client.identity}")
|
|
37
|
+
@client_registry.check_in(client)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Generate new authentication credentials for a client.
|
|
41
|
+
# Returns a Hash containing the credentials required for the client to
|
|
42
|
+
# connect and authenticate with this Server
|
|
43
|
+
def create_client
|
|
44
|
+
@networking.create_client
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Return ClientInfo on which Client sent the given Message
|
|
48
|
+
def client_who_sent(message)
|
|
49
|
+
@client_registry.find(message.from)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Broadcast a +message+ to all clients who match the given +filter+.
|
|
53
|
+
# By default all clients will be notified.
|
|
54
|
+
def publish_message(message, filter = Communication::ClientFilter.new)
|
|
55
|
+
Pantry.logger.debug("[#{@identity}] Publishing #{message.inspect} to #{filter.stream.inspect}")
|
|
56
|
+
message.to = filter.stream
|
|
57
|
+
@networking.publish_message(message)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Callback from the network when a message is received unsolicited from a client.
|
|
61
|
+
# If the message received is unhandleable by this Server, the message is forwarded
|
|
62
|
+
# on down to the clients who match the message's +to+.
|
|
63
|
+
def receive_message(message)
|
|
64
|
+
Pantry.logger.debug("[#{@identity}] Received message #{message.inspect}")
|
|
65
|
+
if @commands.can_handle?(message)
|
|
66
|
+
results = @commands.process(message)
|
|
67
|
+
|
|
68
|
+
if message.requires_response?
|
|
69
|
+
Pantry.logger.debug("[#{@identity}] Returning results #{results.inspect}")
|
|
70
|
+
send_results_back_to_requester(message, results)
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
matched_clients = @client_registry.all_matching(message.to).map(&:identity)
|
|
74
|
+
|
|
75
|
+
Pantry.logger.debug("[#{@identity}] Forwarding message on. Expect response from #{matched_clients.inspect}")
|
|
76
|
+
send_results_back_to_requester(message, matched_clients, true)
|
|
77
|
+
forward_message(message)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Send a Pantry::Message but mark it as requiring a response.
|
|
82
|
+
# This will set up and return a Celluloid::Future that will contain the
|
|
83
|
+
# response once it is available.
|
|
84
|
+
def send_request(client, message)
|
|
85
|
+
message.requires_response!
|
|
86
|
+
message.to = client.identity
|
|
87
|
+
|
|
88
|
+
Pantry.logger.debug("[#{@identity}] Sending request #{message.inspect}")
|
|
89
|
+
|
|
90
|
+
@networking.send_request(message)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Start a FileService::SendFile worker to upload the contents of the
|
|
94
|
+
# file at +file_path+ to the equivalent ReceiveFile at +receiver_uuid+.
|
|
95
|
+
# Using this method requires asking the receiving end (Server or Client) to receive
|
|
96
|
+
# a file first, which will return the +receiver_uuid+ and +file_uuid+ to use here.
|
|
97
|
+
def send_file(file_path, receiver_uuid, file_uuid)
|
|
98
|
+
@networking.send_file(file_path, receiver_uuid, file_uuid)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Set up a FileService::ReceiveFile worker to begin receiving a file with
|
|
102
|
+
# the given size and checksum. This returns an informational object with
|
|
103
|
+
# the new receiver's identity and the file UUID so a SendFile worker knows who
|
|
104
|
+
# to send the file contents to.
|
|
105
|
+
def receive_file(file_size, file_checksum)
|
|
106
|
+
@networking.receive_file(file_size, file_checksum)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
protected
|
|
110
|
+
|
|
111
|
+
def current_hostname
|
|
112
|
+
Socket.gethostname
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def send_results_back_to_requester(message, results, client_response_list = false)
|
|
116
|
+
response_message = message.build_response
|
|
117
|
+
response_message.from = Pantry::SERVER_IDENTITY
|
|
118
|
+
response_message[:client_response_list] = client_response_list
|
|
119
|
+
|
|
120
|
+
[results].flatten(1).each do |entry|
|
|
121
|
+
response_message << entry
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
@networking.publish_message(response_message)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def forward_message(message)
|
|
128
|
+
@networking.forward_message(message)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Require this file to grab the Pantry acceptance test environment.
|
|
3
|
+
# Acceptance tests run a full network of server and multiple clients,
|
|
4
|
+
# and are normally run via a CLI.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require 'pantry'
|
|
8
|
+
require 'celluloid/test'
|
|
9
|
+
require 'pantry/test/support/minitest'
|
|
10
|
+
require 'pantry/test/support/matchers'
|
|
11
|
+
require 'pantry/test/support/mock_ui'
|
|
12
|
+
require 'pantry/test/support/fake_fs'
|
|
13
|
+
|
|
14
|
+
# Catch and show all exceptions thrown during a test run
|
|
15
|
+
$all_exceptions = []
|
|
16
|
+
Celluloid.exception_handler do |exception|
|
|
17
|
+
$all_exceptions << exception
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Minitest.after_run do
|
|
21
|
+
$all_exceptions.each do |exception|
|
|
22
|
+
puts exception
|
|
23
|
+
puts exception.backtrace.join("\n")
|
|
24
|
+
puts ""
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Set up a Server-only echo command so we can differentiate between
|
|
29
|
+
# Client requests and Server requests in the acceptance tests.
|
|
30
|
+
class ServerEchoCommand < Pantry::Commands::Echo
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
module PantryAcceptanceHelpers
|
|
34
|
+
def configure_pantry(ports_start_at: 10101, heartbeat: 300, security: nil)
|
|
35
|
+
Pantry.config.server_host = "127.0.0.1"
|
|
36
|
+
Pantry.config.pub_sub_port = ports_start_at
|
|
37
|
+
Pantry.config.receive_port = ports_start_at + 1
|
|
38
|
+
Pantry.config.file_service_port = ports_start_at + 2
|
|
39
|
+
Pantry.config.client_heartbeat_interval = heartbeat
|
|
40
|
+
Pantry.config.response_timeout = 5
|
|
41
|
+
Pantry.config.security = security
|
|
42
|
+
|
|
43
|
+
begin
|
|
44
|
+
Pantry.add_server_command(ServerEchoCommand)
|
|
45
|
+
rescue Pantry::DuplicateCommandError
|
|
46
|
+
# Already registered
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Set up a fully functional Server + 2 Client environment on the given ports
|
|
51
|
+
# Make sure that the ports given are different for each test or port-conflict
|
|
52
|
+
# errors will happen. Tests should also have a wide enough range between their ports,
|
|
53
|
+
# to ensure there's room for the current setup and any later expansion (10 is a good number).
|
|
54
|
+
#
|
|
55
|
+
# This helper exposes @server, @client1, and @client2 for use in tests
|
|
56
|
+
def set_up_environment(ports_start_at: 10101, heartbeat: 300, security: nil)
|
|
57
|
+
Celluloid.boot
|
|
58
|
+
|
|
59
|
+
configure_pantry(ports_start_at: ports_start_at, heartbeat: heartbeat, security: security)
|
|
60
|
+
|
|
61
|
+
@server = Pantry::Server.new
|
|
62
|
+
@server.identity = "Test Server"
|
|
63
|
+
@server.run
|
|
64
|
+
|
|
65
|
+
@client1 = Pantry::Client.new identity: "client1", application: "pantry", environment: "test", roles: ["app1"]
|
|
66
|
+
@client1.run
|
|
67
|
+
|
|
68
|
+
@client2 = Pantry::Client.new identity: "client2", application: "pantry", environment: "test", roles: ["app2"]
|
|
69
|
+
@client2.run
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def after_teardown
|
|
73
|
+
@client1.shutdown if @client1
|
|
74
|
+
@client2.shutdown if @client2
|
|
75
|
+
@server.shutdown if @server
|
|
76
|
+
|
|
77
|
+
Celluloid.shutdown rescue nil
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Minitest::Test
|
|
82
|
+
include PantryAcceptanceHelpers
|
|
83
|
+
end
|