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,97 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# The communication layer of a Pantry::Server
|
|
5
|
+
# This class manages all of the ZeroMQ sockets and underlying
|
|
6
|
+
# communication systems, handling the sending and receiving of messages.
|
|
7
|
+
class Server
|
|
8
|
+
include Celluloid
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# +listener+ must respond to the #receive_message method
|
|
12
|
+
def initialize(listener)
|
|
13
|
+
@listener = listener
|
|
14
|
+
@response_wait_list = Communication::WaitList.new
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Start up the networking layer, opening up sockets and getting
|
|
18
|
+
# ready for client communication.
|
|
19
|
+
def run
|
|
20
|
+
@security = Communication::Security.new_server
|
|
21
|
+
@security.link_to(self)
|
|
22
|
+
|
|
23
|
+
@publish_socket = Communication::PublishSocket.new_link(
|
|
24
|
+
Pantry.config.server_host,
|
|
25
|
+
Pantry.config.pub_sub_port,
|
|
26
|
+
@security
|
|
27
|
+
)
|
|
28
|
+
@publish_socket.open
|
|
29
|
+
|
|
30
|
+
@receive_socket = Communication::ReceiveSocket.new_link(
|
|
31
|
+
Pantry.config.server_host,
|
|
32
|
+
Pantry.config.receive_port,
|
|
33
|
+
@security
|
|
34
|
+
)
|
|
35
|
+
@receive_socket.add_listener(self)
|
|
36
|
+
@receive_socket.open
|
|
37
|
+
|
|
38
|
+
@file_service = Communication::FileService.new_link(
|
|
39
|
+
Pantry.config.server_host,
|
|
40
|
+
Pantry.config.file_service_port,
|
|
41
|
+
@security
|
|
42
|
+
)
|
|
43
|
+
@file_service.start_server
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Ask Security to generate a new set of credentials as necessary
|
|
47
|
+
# for a new Client to connect to this Server
|
|
48
|
+
def create_client
|
|
49
|
+
@security.create_client
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Send a request to all clients, expecting a result. Returns a Future
|
|
53
|
+
# which can be queried later for the client response.
|
|
54
|
+
def send_request(message)
|
|
55
|
+
@response_wait_list.wait_for(message).tap do
|
|
56
|
+
publish_message(message)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Send a message to all connected subscribers without modifying the package.
|
|
61
|
+
# Used when handling requests meant for other clients (say from the CLI). The source
|
|
62
|
+
# is untouched so the Client(s) handling know how to respond.
|
|
63
|
+
def forward_message(message)
|
|
64
|
+
message.forwarded!
|
|
65
|
+
publish_message(message)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Send a message to all clients who match the given filter.
|
|
69
|
+
def publish_message(message)
|
|
70
|
+
message.from ||= @listener
|
|
71
|
+
@publish_socket.send_message(message)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Listener callback from ReceiveSocket. See if we need to match this response
|
|
75
|
+
# with a previous request or if it's a new message entirely.
|
|
76
|
+
def handle_message(message)
|
|
77
|
+
if message.forwarded?
|
|
78
|
+
forward_message(message)
|
|
79
|
+
elsif @response_wait_list.waiting_for?(message)
|
|
80
|
+
@response_wait_list.received(message)
|
|
81
|
+
else
|
|
82
|
+
@listener.receive_message(message)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def receive_file(file_size, file_checksum)
|
|
87
|
+
@file_service.receive_file(file_size, file_checksum)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def send_file(file_path, receiver_uuid, file_uuid)
|
|
91
|
+
@file_service.send_file(file_path, receiver_uuid, file_uuid)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# The SubscribeSocket manages the Subscription side of the Pub/Sub channel,
|
|
5
|
+
# using a 0MQ PUB socket. This socket can subscribe to any number of streams
|
|
6
|
+
# depending on the filtering given. Messages received by this socket are passed
|
|
7
|
+
# to the configured listener as Messages.
|
|
8
|
+
class SubscribeSocket < ReadingSocket
|
|
9
|
+
|
|
10
|
+
def initialize(host, port, security)
|
|
11
|
+
super
|
|
12
|
+
@filter = ClientFilter.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def filter_on(client_filter)
|
|
16
|
+
@filter = client_filter
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def build_socket
|
|
20
|
+
Celluloid::ZMQ::SubSocket.new
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def open_socket(socket)
|
|
24
|
+
socket.connect("tcp://#{host}:#{port}")
|
|
25
|
+
|
|
26
|
+
@filter.streams.each do |stream|
|
|
27
|
+
socket.subscribe(stream)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# The WaitList manages futures for asynchronously waiting for responses
|
|
5
|
+
# from either the Client or the Server. Given an identity and a message,
|
|
6
|
+
# WaitList returns a Future that will be filled when the handler in question
|
|
7
|
+
# receives a message of the same Message type from that identity.
|
|
8
|
+
class WaitList
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@futures = Hash.new {|hash, key| hash[key] = []}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Given a +message+ being sent, create a Future for a response to this message.
|
|
15
|
+
# This keys off of the Message's UUID, which must be kept in tact as it
|
|
16
|
+
# passes through the system.
|
|
17
|
+
def wait_for(message)
|
|
18
|
+
future = Celluloid::Future.new
|
|
19
|
+
@futures[ message.uuid ] << future
|
|
20
|
+
future
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Is there a future waiting for this response message?
|
|
24
|
+
def waiting_for?(message)
|
|
25
|
+
!@futures[ message.uuid ].empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Internal to Celluloid::Future, using #signal ends up in a Result object
|
|
29
|
+
# in which calling #value then calls #value on the saved data which in our
|
|
30
|
+
# case is Message. We just want the Message so wrap up our messages in this
|
|
31
|
+
# object to work around this oddity.
|
|
32
|
+
#
|
|
33
|
+
# https://github.com/celluloid/celluloid/blob/master/lib/celluloid/future.rb#L89
|
|
34
|
+
FutureResultWrapper = Struct.new(:value)
|
|
35
|
+
|
|
36
|
+
def received(message)
|
|
37
|
+
if future = @futures[ message.uuid ].shift
|
|
38
|
+
future.signal(FutureResultWrapper.new(message))
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
module Communication
|
|
3
|
+
|
|
4
|
+
# Base class of all sockets that write messages through ZMQ.
|
|
5
|
+
# Not meant for direct use, please use one of the subclasses for specific
|
|
6
|
+
# functionality.
|
|
7
|
+
class WritingSocket
|
|
8
|
+
include Celluloid::ZMQ
|
|
9
|
+
|
|
10
|
+
attr_reader :host, :port
|
|
11
|
+
|
|
12
|
+
def initialize(host, port, security)
|
|
13
|
+
@host = host
|
|
14
|
+
@port = port
|
|
15
|
+
@security = security
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def open
|
|
19
|
+
@socket = build_socket
|
|
20
|
+
Communication.configure_socket(@socket)
|
|
21
|
+
@security.configure_socket(@socket)
|
|
22
|
+
|
|
23
|
+
open_socket(@socket)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def build_socket
|
|
27
|
+
raise "Implement the socket setup."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def open_socket(socket)
|
|
31
|
+
raise "Connect / Bind the socket built in #build_socket"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def close
|
|
35
|
+
@socket.close if @socket
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def send_message(message)
|
|
39
|
+
@socket.write(
|
|
40
|
+
SerializeMessage.to_zeromq(message)
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# Retrieve the current configuration set
|
|
4
|
+
def self.config
|
|
5
|
+
@@config ||= Config.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.reset_config!
|
|
9
|
+
@@config = nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Global configuration values for running all of Pantry.
|
|
13
|
+
class Config
|
|
14
|
+
##
|
|
15
|
+
# Global Configuration
|
|
16
|
+
##
|
|
17
|
+
|
|
18
|
+
# Where does Pantry log to?
|
|
19
|
+
# Can be "stdout", "syslog", or a file system path
|
|
20
|
+
# Defaults to STDOUT
|
|
21
|
+
# When using syslog, program name will be "pantry"
|
|
22
|
+
attr_accessor :log_to
|
|
23
|
+
|
|
24
|
+
# After what level are logs dropped and ignored?
|
|
25
|
+
# Can be any of: "fatal", "error", "warn", "info", "debug"
|
|
26
|
+
# Each level will include the logs of all levels above it.
|
|
27
|
+
# Defaults to "info"
|
|
28
|
+
attr_accessor :log_level
|
|
29
|
+
|
|
30
|
+
# If logging to Syslog, set the program-name Pantry will
|
|
31
|
+
# use when sending logs to syslog.
|
|
32
|
+
# Defaults to "pantry"
|
|
33
|
+
attr_accessor :syslog_program_name
|
|
34
|
+
|
|
35
|
+
# Location on the file system Pantry will store any persistent data
|
|
36
|
+
# Default: /var/lib/pantry
|
|
37
|
+
attr_accessor :root_dir
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Communication Configuration
|
|
41
|
+
##
|
|
42
|
+
|
|
43
|
+
# Host name of the Pantry Server
|
|
44
|
+
attr_accessor :server_host
|
|
45
|
+
|
|
46
|
+
# Port used for Pub/Sub communication
|
|
47
|
+
attr_accessor :pub_sub_port
|
|
48
|
+
|
|
49
|
+
# Port clients use to send information to the Server
|
|
50
|
+
attr_accessor :receive_port
|
|
51
|
+
|
|
52
|
+
# Port through which files are sent and received
|
|
53
|
+
attr_accessor :file_service_port
|
|
54
|
+
|
|
55
|
+
# How often, in seconds, the client pings the Server
|
|
56
|
+
attr_accessor :client_heartbeat_interval
|
|
57
|
+
|
|
58
|
+
# What type of security will Pantry be employing?
|
|
59
|
+
# Available types are nil (no security) and "curve" (ZMQ4 Curve security)
|
|
60
|
+
#
|
|
61
|
+
# Defaults to nil because curve security has not yet been fully
|
|
62
|
+
# vetted by the crypto-community
|
|
63
|
+
attr_accessor :security
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Client Identification
|
|
67
|
+
##
|
|
68
|
+
|
|
69
|
+
# Unique identification of this Client
|
|
70
|
+
attr_accessor :client_identity
|
|
71
|
+
|
|
72
|
+
# Application this Client serves
|
|
73
|
+
attr_accessor :client_application
|
|
74
|
+
|
|
75
|
+
# Environment of the Application this Client runs
|
|
76
|
+
attr_accessor :client_environment
|
|
77
|
+
|
|
78
|
+
# Roles this Client serves
|
|
79
|
+
attr_accessor :client_roles
|
|
80
|
+
|
|
81
|
+
##
|
|
82
|
+
# Testing configuration helpers
|
|
83
|
+
##
|
|
84
|
+
|
|
85
|
+
# Time in seconds the CLI will wait for a response from the server
|
|
86
|
+
# By default this is nil, meaning unlimited timeout. Used mainly in tests.
|
|
87
|
+
attr_accessor :response_timeout
|
|
88
|
+
|
|
89
|
+
def initialize
|
|
90
|
+
|
|
91
|
+
# Logging defaults
|
|
92
|
+
@log_level = "info"
|
|
93
|
+
@syslog_program_name = "pantry"
|
|
94
|
+
|
|
95
|
+
# Default connectivity settings
|
|
96
|
+
@server_host = "127.0.0.1"
|
|
97
|
+
@pub_sub_port = 23001
|
|
98
|
+
@receive_port = 23002
|
|
99
|
+
@file_service_port = 23003
|
|
100
|
+
|
|
101
|
+
# Default client heartbeat to every 5 minutes
|
|
102
|
+
@client_heartbeat_interval = 300
|
|
103
|
+
|
|
104
|
+
# Default Client identificiation values
|
|
105
|
+
@client_identity = nil
|
|
106
|
+
@client_application = nil
|
|
107
|
+
@client_environment = nil
|
|
108
|
+
@client_roles = []
|
|
109
|
+
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Given a YAML config file, read in config values
|
|
113
|
+
def load_file(config_file)
|
|
114
|
+
configs = SafeYAML.load_file(config_file)
|
|
115
|
+
load_global_configs(configs)
|
|
116
|
+
load_networking_configs(configs["networking"])
|
|
117
|
+
load_client_configs(configs["client"])
|
|
118
|
+
refresh
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def refresh
|
|
122
|
+
apply_configuration
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
protected
|
|
126
|
+
|
|
127
|
+
def load_global_configs(configs)
|
|
128
|
+
@log_to = configs["log_to"]
|
|
129
|
+
|
|
130
|
+
if configs["log_level"]
|
|
131
|
+
@log_level = configs["log_level"]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
if configs["syslog_program_name"]
|
|
135
|
+
@syslog_program_name = configs["syslog_program_name"]
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
@root_dir = configs["root_dir"]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def load_networking_configs(configs)
|
|
142
|
+
return unless configs
|
|
143
|
+
|
|
144
|
+
if configs["server_host"]
|
|
145
|
+
@server_host = configs["server_host"]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if configs["pub_sub_port"]
|
|
149
|
+
@pub_sub_port = configs["pub_sub_port"]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if configs["receive_port"]
|
|
153
|
+
@receive_port = configs["receive_port"]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if configs["file_service_port"]
|
|
157
|
+
@file_service_port = configs["file_service_port"]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
@security = configs["security"]
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def load_client_configs(configs)
|
|
164
|
+
return unless configs
|
|
165
|
+
|
|
166
|
+
@client_identity = configs["identity"]
|
|
167
|
+
@client_application = configs["application"]
|
|
168
|
+
@client_environment = configs["environment"]
|
|
169
|
+
@client_roles = configs["roles"]
|
|
170
|
+
|
|
171
|
+
if configs["heartbeat_interval"]
|
|
172
|
+
@client_heartbeat_interval = configs["heartbeat_interval"]
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def apply_configuration
|
|
177
|
+
# Reset our logger knowledge so the next call picks up the
|
|
178
|
+
# new configs
|
|
179
|
+
Pantry.logger = nil
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# Use EDITOR to edit the contents of a remote file locally
|
|
4
|
+
# The editor can validate the updated content to be YAML (more to be added as needed)
|
|
5
|
+
# and will show errors and re-edit the file if validation fails.
|
|
6
|
+
#
|
|
7
|
+
# If the user chooses to cancel editing, #edit will return the original
|
|
8
|
+
# content given to it.
|
|
9
|
+
#
|
|
10
|
+
# Usage is simple:
|
|
11
|
+
#
|
|
12
|
+
# editor = FileEditor.new
|
|
13
|
+
# updated_content = editor.edit(file_contents, file_type)
|
|
14
|
+
#
|
|
15
|
+
class FileEditor
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@editor = ENV['EDITOR']
|
|
19
|
+
raise "Please set EDITOR environment variable to a text editor." unless @editor
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def edit(original_content, file_type)
|
|
23
|
+
file = create_temp_file(original_content, file_type)
|
|
24
|
+
new_content = ""
|
|
25
|
+
|
|
26
|
+
loop do
|
|
27
|
+
new_content = edit_file(file)
|
|
28
|
+
|
|
29
|
+
is_valid, message = validate_content(new_content, file_type)
|
|
30
|
+
break if is_valid
|
|
31
|
+
|
|
32
|
+
Pantry.ui.say(message)
|
|
33
|
+
if !Pantry.ui.continue?("Continue editing?")
|
|
34
|
+
new_content = original_content
|
|
35
|
+
break
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
file.unlink
|
|
40
|
+
new_content
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
protected
|
|
44
|
+
|
|
45
|
+
def create_temp_file(file_contents, file_type)
|
|
46
|
+
tempfile = Tempfile.new(["edit-in-line", ".#{file_type}"])
|
|
47
|
+
tempfile.write(file_contents)
|
|
48
|
+
tempfile.close
|
|
49
|
+
tempfile
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def edit_file(tempfile)
|
|
53
|
+
system("#{@editor} #{tempfile.path}")
|
|
54
|
+
File.read(tempfile.path)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def validate_content(content, file_type)
|
|
58
|
+
begin
|
|
59
|
+
Psych.parse(content, "config.yml")
|
|
60
|
+
return true, nil
|
|
61
|
+
rescue => ex
|
|
62
|
+
return false, ex.message
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|