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,53 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# Manages and processes commands as requested from the Client or the Server.
|
|
4
|
+
# Given a mapping of available commands, maps the incoming message to the appropriate
|
|
5
|
+
# command handler and returns the response. Returns nil if no command found.
|
|
6
|
+
class CommandHandler
|
|
7
|
+
|
|
8
|
+
def initialize(server_or_client, commands_to_register = [])
|
|
9
|
+
@handlers = {}
|
|
10
|
+
@server_or_client = server_or_client
|
|
11
|
+
|
|
12
|
+
commands_to_register.each do |command_class|
|
|
13
|
+
add_command(command_class)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Install a Command class as a message handler for this process.
|
|
18
|
+
# The Message's +type+ for this kind of message is simply the name of the class
|
|
19
|
+
# without any scope information. E.g. Echo not Pantry::Command::Echo.
|
|
20
|
+
def add_command(command_class)
|
|
21
|
+
@handlers[command_class.message_type] = build_command_proc(command_class)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Does this CommandHandler know how to handle the given command?
|
|
25
|
+
def can_handle?(message)
|
|
26
|
+
!@handlers[message.type].nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Given a message, figure out which handler should be triggered and get things rolling
|
|
30
|
+
def process(message)
|
|
31
|
+
if handler = @handlers[message.type]
|
|
32
|
+
Pantry.logger.debug("[#{@server_or_client.identity}] Running handler on #{message.inspect}")
|
|
33
|
+
handler.call(message)
|
|
34
|
+
else
|
|
35
|
+
Pantry.logger.warn(
|
|
36
|
+
"[#{@server_or_client.identity}] No known handler for message type #{message.type}"
|
|
37
|
+
)
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
protected
|
|
43
|
+
|
|
44
|
+
def build_command_proc(command_class)
|
|
45
|
+
proc do |message|
|
|
46
|
+
command_obj = command_class.new
|
|
47
|
+
command_obj.server_or_client = @server_or_client
|
|
48
|
+
command_obj.perform(message)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
class CommandLine
|
|
4
|
+
|
|
5
|
+
# The top-level set of CLI options and flags Pantry respects
|
|
6
|
+
BASE_OPTIONS = proc {
|
|
7
|
+
banner "Usage: #{File.basename($0)} [options] [command [command options]]"
|
|
8
|
+
option "-h", "--host HOSTNAME", String, "Hostname of the Server to connect to"
|
|
9
|
+
option "--curve-key-file FILE", String, "Name of the file in .pantry holding Curve keys.",
|
|
10
|
+
"Specifying this option will turn on Curve encryption."
|
|
11
|
+
|
|
12
|
+
option "-a", "--application APPLICATION", String, "Filter Clients by a specific APPLICATION"
|
|
13
|
+
option "-e", "--environment ENVIRONMENT", String, "Filter Clients by a specific ENVIRONMENT"
|
|
14
|
+
option "-r", "--roles ROLE1,ROLE2", Array, "Filter Clients by given ROLES"
|
|
15
|
+
option "-v", "--verbose", "Verbose output (INFO)"
|
|
16
|
+
option "-d", "--debug", "Even more Verbose output (DEBUG)"
|
|
17
|
+
option "-V", "--version", "Print out Pantry's version"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
def initialize(command_line)
|
|
21
|
+
@command_line = command_line
|
|
22
|
+
@known_commands = {}
|
|
23
|
+
|
|
24
|
+
@commands = find_all_cli_enabled_commands
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Parse the full command line. Returns a hash containing the options found
|
|
28
|
+
# as well as what is still left on the command line.
|
|
29
|
+
# If the command line is empty, will default to --help.
|
|
30
|
+
#
|
|
31
|
+
# Returns [nil, nil] if help was requested or there was a problem.
|
|
32
|
+
def parse!
|
|
33
|
+
@command_line = merge_command_line_with_defaults(@command_line)
|
|
34
|
+
parser = build_parser(@commands)
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
if @command_line.empty?
|
|
38
|
+
@command_line << "--help"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@parsed_options = parser.parse!(@command_line)
|
|
42
|
+
|
|
43
|
+
if @parsed_options['help']
|
|
44
|
+
# Help printed already
|
|
45
|
+
return [nil, nil]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
[@parsed_options, @command_line]
|
|
49
|
+
rescue => ex
|
|
50
|
+
puts ex, ""
|
|
51
|
+
puts parser.help
|
|
52
|
+
[nil, nil]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns details of the command found during parsing.
|
|
57
|
+
# Returns a hash with the keys +banner+ and +class+,
|
|
58
|
+
# or returns nil if no matching command was found
|
|
59
|
+
def triggered_command
|
|
60
|
+
[
|
|
61
|
+
@commands[@parsed_options.command_found],
|
|
62
|
+
@parsed_options[@parsed_options.command_found]
|
|
63
|
+
]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
protected
|
|
67
|
+
|
|
68
|
+
def find_all_cli_enabled_commands
|
|
69
|
+
commands = {}
|
|
70
|
+
Pantry.all_commands.each do |command_class|
|
|
71
|
+
if command_class.command_name
|
|
72
|
+
# Hmm duplicated from OptParsePlus
|
|
73
|
+
base_command_name = command_class.command_name.split(/\s/).first
|
|
74
|
+
commands[base_command_name] = {
|
|
75
|
+
banner: command_class.command_name,
|
|
76
|
+
class: command_class
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
commands
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def merge_command_line_with_defaults(command_line)
|
|
85
|
+
[read_defaults_file, command_line].flatten
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def read_defaults_file
|
|
89
|
+
dot_pantry_config = Pantry.root.join("config")
|
|
90
|
+
|
|
91
|
+
if File.exist?(dot_pantry_config)
|
|
92
|
+
# ARGV is an array of the command line seperated by white-space.
|
|
93
|
+
# Make sure what we read from .pantry returns the same
|
|
94
|
+
File.readlines(dot_pantry_config).map { |line|
|
|
95
|
+
line.strip.split(/\s/)
|
|
96
|
+
}.flatten
|
|
97
|
+
else
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def build_parser(cli_commands)
|
|
103
|
+
parser = OptParsePlus.new
|
|
104
|
+
parser.add_options(&BASE_OPTIONS)
|
|
105
|
+
|
|
106
|
+
cli_commands.each do |base_command_name, command_info|
|
|
107
|
+
parser.add_command(command_info[:banner], &command_info[:class].command_config)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
parser
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
# Ask the server to generate a new set of keys
|
|
5
|
+
# Prints a yaml file that contains the required keys for a client to properly
|
|
6
|
+
# conenct and authenticate to the server
|
|
7
|
+
class CreateClient < Pantry::Command
|
|
8
|
+
command "client:create" do
|
|
9
|
+
description "Generate a new public/private encryption keypair for a client."
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def perform(message)
|
|
13
|
+
server.create_client
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def receive_server_response(message)
|
|
17
|
+
keys = message.body[0]
|
|
18
|
+
Pantry.ui.say("New Client Credentials")
|
|
19
|
+
Pantry.ui.say("Store this in the Client's Pantry.root/security/curve/client_keys.yml")
|
|
20
|
+
Pantry.ui.say(YAML.dump({
|
|
21
|
+
"server_public_key" => keys[:server_public_key],
|
|
22
|
+
"public_key" => keys[:public_key],
|
|
23
|
+
"private_key" => keys[:private_key]
|
|
24
|
+
}))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
# Download all content inside of the given directory.
|
|
5
|
+
#
|
|
6
|
+
# This command expects simple directories with a small number of files that are
|
|
7
|
+
# themselves small in size, as this command reads every file into memory and sends
|
|
8
|
+
# that raw content back to the Client. If there are more substantial files to transfer
|
|
9
|
+
# use #send_file and #receive_file instead.
|
|
10
|
+
class DownloadDirectory < Pantry::Command
|
|
11
|
+
|
|
12
|
+
def initialize(directory = nil)
|
|
13
|
+
@directory = directory
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_message
|
|
17
|
+
super.tap do |message|
|
|
18
|
+
message << @directory.to_s
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform(message)
|
|
23
|
+
directory = Pantry.root.join(message.body[0])
|
|
24
|
+
|
|
25
|
+
Dir[directory.join("**", "*")].map do |file|
|
|
26
|
+
next if File.directory?(file)
|
|
27
|
+
[Pathname.new(file).relative_path_from(directory).to_s,
|
|
28
|
+
File.read(file)]
|
|
29
|
+
end.compact
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
# Simple Echo command, returns the body of the Message given.
|
|
5
|
+
class Echo < Command
|
|
6
|
+
|
|
7
|
+
command "echo MESSAGE" do
|
|
8
|
+
description "Test Client communication with a simple Echo request"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(string_to_echo = "")
|
|
12
|
+
@string_to_echo = string_to_echo
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_message
|
|
16
|
+
message = super
|
|
17
|
+
message << @string_to_echo
|
|
18
|
+
message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def perform(message)
|
|
22
|
+
message.body[0]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def receive_client_response(response)
|
|
26
|
+
Pantry.ui.say("#{response.from} echo's #{response.body[0].inspect}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
# Edit the configuration of the requested Application.
|
|
5
|
+
#
|
|
6
|
+
# Application configuration is stored on the Server under
|
|
7
|
+
# Pantry.root/applications/[app name]/config.yml and is where all
|
|
8
|
+
# configuration lives for how Pantry manages this application.
|
|
9
|
+
class EditApplication < Pantry::Command
|
|
10
|
+
|
|
11
|
+
command "edit" do
|
|
12
|
+
description "Edit an application's configuration with the text editor specified in $EDITOR.
|
|
13
|
+
Requires an Application."
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def prepare_message(options)
|
|
17
|
+
@application = options[:application]
|
|
18
|
+
raise Pantry::MissingOption, 'Missing required option "application"' unless @application
|
|
19
|
+
|
|
20
|
+
# Let the EDITOR check run before we do any communication
|
|
21
|
+
@editor = Pantry::FileEditor.new
|
|
22
|
+
|
|
23
|
+
super.tap do |msg|
|
|
24
|
+
msg << @application
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Read or create a new config file for the given application
|
|
29
|
+
# and return the contents of this config file, which will always be
|
|
30
|
+
# a YAML document
|
|
31
|
+
def perform(message)
|
|
32
|
+
application = message.body[0]
|
|
33
|
+
|
|
34
|
+
config_file = Pantry.root.join("applications", application, "config.yml")
|
|
35
|
+
FileUtils.mkdir_p(File.dirname(config_file))
|
|
36
|
+
|
|
37
|
+
if File.exists?(config_file)
|
|
38
|
+
[File.read(config_file)]
|
|
39
|
+
else
|
|
40
|
+
[{"name" => application}.to_yaml]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def receive_server_response(message)
|
|
45
|
+
current_config = message.body[0]
|
|
46
|
+
new_config = @editor.edit(current_config, :yaml)
|
|
47
|
+
|
|
48
|
+
if new_config != current_config
|
|
49
|
+
send_request!(
|
|
50
|
+
Pantry::Commands::UpdateApplication.new(
|
|
51
|
+
@application, new_config
|
|
52
|
+
).to_message
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
class RegisterClient < Command
|
|
5
|
+
|
|
6
|
+
def initialize(client_info = nil)
|
|
7
|
+
@client_info = client_info
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_message
|
|
11
|
+
message = super
|
|
12
|
+
message << {
|
|
13
|
+
application: @client_info.application,
|
|
14
|
+
environment: @client_info.environment,
|
|
15
|
+
roles: @client_info.roles
|
|
16
|
+
}
|
|
17
|
+
message
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Take note that a Client has connected and registered itself
|
|
21
|
+
# with this Server.
|
|
22
|
+
def perform(message)
|
|
23
|
+
details = message.body[0]
|
|
24
|
+
|
|
25
|
+
@client_info = Pantry::ClientInfo.new(
|
|
26
|
+
identity: message.from,
|
|
27
|
+
application: details[:application],
|
|
28
|
+
environment: details[:environment],
|
|
29
|
+
roles: details[:roles]
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
self.server.register_client(@client_info)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Commands
|
|
3
|
+
|
|
4
|
+
class Status < Command
|
|
5
|
+
|
|
6
|
+
command "status" do
|
|
7
|
+
description "List all Clients that match the options"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_accessor :client_filter
|
|
11
|
+
|
|
12
|
+
def prepare_message(options)
|
|
13
|
+
@client_filter = Pantry::Communication::ClientFilter.new(
|
|
14
|
+
application: options[:application],
|
|
15
|
+
environment: options[:environment],
|
|
16
|
+
roles: options[:roles]
|
|
17
|
+
)
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_message
|
|
22
|
+
message = super
|
|
23
|
+
message << @client_filter.to_hash
|
|
24
|
+
message
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Return information about all connected Clients that match the given filter
|
|
28
|
+
def perform(message)
|
|
29
|
+
@client_filter = Pantry::Communication::ClientFilter.new(**(message.body[0] || {}))
|
|
30
|
+
self.server.client_registry.all_matching(@client_filter) do |client, record|
|
|
31
|
+
{
|
|
32
|
+
identity: client.identity,
|
|
33
|
+
application: client.application,
|
|
34
|
+
environment: client.environment,
|
|
35
|
+
roles: client.roles,
|
|
36
|
+
last_checked_in: record.last_checked_in_at
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def receive_server_response(message)
|
|
42
|
+
output =
|
|
43
|
+
clients = message.body.map do |client|
|
|
44
|
+
[
|
|
45
|
+
time_ago_in_words(client[:last_checked_in]),
|
|
46
|
+
client[:identity],
|
|
47
|
+
"|",
|
|
48
|
+
client[:application],
|
|
49
|
+
client[:environment],
|
|
50
|
+
[client[:roles]].flatten.join(",")
|
|
51
|
+
].compact.join(" ")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Pantry.ui.list(output)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
protected
|
|
58
|
+
|
|
59
|
+
def time_ago_in_words(time)
|
|
60
|
+
now = DateTime.now.to_time
|
|
61
|
+
checked_in = DateTime.parse(time).to_time
|
|
62
|
+
|
|
63
|
+
seconds_since = (now - checked_in).to_i
|
|
64
|
+
case seconds_since
|
|
65
|
+
when 0..(2*60)
|
|
66
|
+
Pantry.ui.color("A minute ago", :green)
|
|
67
|
+
when (2*60+1)..(59*60)
|
|
68
|
+
Pantry.ui.color("#{seconds_since / 60} minutes ago", :green)
|
|
69
|
+
else
|
|
70
|
+
hours_since = seconds_since / 60 / 60
|
|
71
|
+
hours_key = hours_since > 1 ? "hours" : "hour"
|
|
72
|
+
Pantry.ui.color("#{hours_since} #{hours_key} ago", :red)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
end
|