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,31 @@
|
|
|
1
|
+
gem 'fakefs'
|
|
2
|
+
require 'fakefs/safe'
|
|
3
|
+
|
|
4
|
+
# Hook up FakeFS into Minitest
|
|
5
|
+
class MiniTest::Test
|
|
6
|
+
def self.fake_fs!
|
|
7
|
+
before do
|
|
8
|
+
FakeFS.activate!
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
after do
|
|
12
|
+
FakeFS.deactivate!
|
|
13
|
+
FakeFS::FileSystem.clear
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Minitest uses Tempfiles for figuring out more complicated diffs
|
|
19
|
+
# This causes FakeFS to explode, so make sure this is run without FakeFS
|
|
20
|
+
# enabled.
|
|
21
|
+
module Minitest
|
|
22
|
+
module Assertions
|
|
23
|
+
alias :actual_diff :diff
|
|
24
|
+
|
|
25
|
+
def diff exp, act
|
|
26
|
+
FakeFS.without do
|
|
27
|
+
actual_diff exp, act
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Some custom MiniTest matchers used throughout the system
|
|
2
|
+
|
|
3
|
+
module Minitest::Assertions
|
|
4
|
+
|
|
5
|
+
# I don't like the refute_ syntax, sorry =/
|
|
6
|
+
|
|
7
|
+
alias assert_not_nil refute_nil
|
|
8
|
+
alias assert_not refute
|
|
9
|
+
alias assert_false refute
|
|
10
|
+
alias assert_not_equal refute_equal
|
|
11
|
+
alias assert_no_match refute_match
|
|
12
|
+
|
|
13
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# For actions that print out or use the console via
|
|
2
|
+
# Pantry.ui, these helpers mock out stdout/stderr to make it easy
|
|
3
|
+
# to grab what's printed and to inject keypresses
|
|
4
|
+
class MiniTest::Test
|
|
5
|
+
def self.mock_ui!
|
|
6
|
+
before do
|
|
7
|
+
@mock_stdin = StringIO.new
|
|
8
|
+
@mock_stdout = StringIO.new
|
|
9
|
+
Pantry.reset_ui!
|
|
10
|
+
Pantry.ui(@mock_stdin, @mock_stdout)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Return the strings added to stdout through the test
|
|
15
|
+
def stdout
|
|
16
|
+
@mock_stdout.string
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Get access to the mock of STDIN to add values
|
|
20
|
+
def stdin
|
|
21
|
+
@mock_stdin
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Require this file to grab the Pantry unit test environment.
|
|
3
|
+
# This environment includes setup helpers to ensure Celluloid is running,
|
|
4
|
+
# as well as a few mocks (ui and fakefs) as well as helpers to facilitate
|
|
5
|
+
# testing Pantry plugins.
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require 'pantry'
|
|
9
|
+
require 'celluloid/test'
|
|
10
|
+
require 'pantry/test/support/minitest'
|
|
11
|
+
require 'pantry/test/support/matchers'
|
|
12
|
+
require 'pantry/test/support/mock_ui'
|
|
13
|
+
require 'pantry/test/support/fake_fs'
|
data/lib/pantry/ui.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Pantry
|
|
2
|
+
|
|
3
|
+
# Global access to Pantry's UI handler. This object offers up
|
|
4
|
+
# a set of methods used to interact with the User via the CLI.
|
|
5
|
+
def self.ui(input = $stdin, output = $stdout)
|
|
6
|
+
@@ui ||= Pantry::UI.new(input, output)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.reset_ui!
|
|
10
|
+
@@ui = nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class UI
|
|
14
|
+
|
|
15
|
+
def initialize(input = $stdin, output = $stdout)
|
|
16
|
+
require 'highline'
|
|
17
|
+
@output = output
|
|
18
|
+
@input = input
|
|
19
|
+
@highline = HighLine.new(input, output)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Send a message to STDOUT
|
|
23
|
+
def say(message)
|
|
24
|
+
@highline.say(message)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Print out a list, attempting to make it look somewhat reasonable
|
|
28
|
+
def list(array)
|
|
29
|
+
say(array.join("\n") + "\n")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def color(string, color)
|
|
33
|
+
HighLine.color(string, color)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Show the user a message and ask them to continue by hitting Enter,
|
|
37
|
+
# or they can cancel with "No"
|
|
38
|
+
def continue?(message)
|
|
39
|
+
@highline.agree(message) do |q|
|
|
40
|
+
q.default = "yes"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Start a new progress meter with the given number of ticks
|
|
45
|
+
def progress_start(tick_count)
|
|
46
|
+
require 'ruby-progressbar'
|
|
47
|
+
@progress = ProgressBar.create(
|
|
48
|
+
total: tick_count, output: @output,
|
|
49
|
+
format: "Progress: %P%% |%B| %c/%C %e"
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Increment the running progress meter the given number of ticks
|
|
54
|
+
def progress_step(tick_count)
|
|
55
|
+
if @progress.progress + tick_count > @progress.total
|
|
56
|
+
tick_count = @progress.total - @progress.progress
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
@progress.progress += tick_count
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Complete and close down the current progress meter
|
|
63
|
+
def progress_finish
|
|
64
|
+
@progress.finish
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
end
|
|
68
|
+
end
|
data/pantry.gemspec
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
2
|
+
require "pantry/version"
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = "pantry"
|
|
6
|
+
s.version = Pantry::VERSION
|
|
7
|
+
s.platform = Gem::Platform::RUBY
|
|
8
|
+
s.authors = ["Collective Idea", "Jason Roelofs"]
|
|
9
|
+
s.email = ["code@collectiveidea.com", "jasongroelofs@gmail.com"]
|
|
10
|
+
s.license = "MIT"
|
|
11
|
+
s.homepage = "http://pantryops.org"
|
|
12
|
+
|
|
13
|
+
s.summary = "Modern DevOps Automation"
|
|
14
|
+
s.description = <<-EOS
|
|
15
|
+
Pantry is a framework that provides answers to common questions encoutered when setting up a DevOps, server configuration, or server provisioning pipeline.
|
|
16
|
+
EOS
|
|
17
|
+
|
|
18
|
+
s.required_ruby_version = ">= 2.0.0"
|
|
19
|
+
|
|
20
|
+
s.files = `git ls-files`.split("\n")
|
|
21
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
|
22
|
+
s.require_path = "lib"
|
|
23
|
+
s.bindir = "bin"
|
|
24
|
+
|
|
25
|
+
s.executables = %w(pantry-client pantry-server pantry)
|
|
26
|
+
|
|
27
|
+
s.requirements << "zeromq 3.x or 4.x"
|
|
28
|
+
s.requirements << "libsodium for Curve security"
|
|
29
|
+
|
|
30
|
+
s.add_runtime_dependency "ffi-rzmq", "~> 2.0", ">= 2.0.0"
|
|
31
|
+
s.add_runtime_dependency "celluloid", "~> 0.15", ">= 0.15.0"
|
|
32
|
+
s.add_runtime_dependency "celluloid-zmq", "~> 0.15", ">= 0.15.0"
|
|
33
|
+
s.add_runtime_dependency "highline", "~> 1.6", ">= 1.6.21"
|
|
34
|
+
s.add_runtime_dependency "json", "~> 1.8", ">= 1.8.1"
|
|
35
|
+
s.add_runtime_dependency "ruby-progressbar", "~> 1.4", ">= 1.4.2"
|
|
36
|
+
s.add_runtime_dependency "safe_yaml", "~> 1.0", ">= 1.0.1"
|
|
37
|
+
|
|
38
|
+
s.add_development_dependency "mocha", "~> 1.0", ">= 1.0.0"
|
|
39
|
+
s.add_development_dependency "fakefs", "~> 0.5", ">= 0.5.1"
|
|
40
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "CLI requests information from individual clients" do
|
|
4
|
+
|
|
5
|
+
mock_ui!
|
|
6
|
+
|
|
7
|
+
it "receives responses from each client asked" do
|
|
8
|
+
set_up_environment(ports_start_at: 10100)
|
|
9
|
+
|
|
10
|
+
Pantry::CLI.new(
|
|
11
|
+
["-a", "pantry", "echo", "This is Neat"],
|
|
12
|
+
identity: "cli1"
|
|
13
|
+
).run
|
|
14
|
+
|
|
15
|
+
assert_match %r|Expecting response from 2 clients|, stdout
|
|
16
|
+
assert_match %r|#{@client1.identity} echo's "This is Neat"|, stdout
|
|
17
|
+
assert_match %r|#{@client2.identity} echo's "This is Neat"|, stdout
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "can target specific clients for the commands sent" do
|
|
21
|
+
set_up_environment(ports_start_at: 10110)
|
|
22
|
+
|
|
23
|
+
Pantry::CLI.new(
|
|
24
|
+
["-a", "pantry", "-e", "test", "-r", "app1", "echo", "This is Neat"],
|
|
25
|
+
identity: "cli1"
|
|
26
|
+
).run
|
|
27
|
+
|
|
28
|
+
assert_match %r|Expecting response from 1 client|, stdout
|
|
29
|
+
assert_match %r|#{@client1.identity} echo's \"This is Neat\"|, stdout
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "CLI can ask Server for information" do
|
|
4
|
+
|
|
5
|
+
mock_ui!
|
|
6
|
+
|
|
7
|
+
it "can ask for all known nodes" do
|
|
8
|
+
set_up_environment(ports_start_at: 10200)
|
|
9
|
+
|
|
10
|
+
Pantry::CLI.new(
|
|
11
|
+
["status"],
|
|
12
|
+
identity: "test_client"
|
|
13
|
+
).run
|
|
14
|
+
|
|
15
|
+
assert_match /#{@client1.identity}/, stdout, "Did not contain line for client1"
|
|
16
|
+
assert_match /#{@client2.identity}/, stdout, "Did not contain line for client2"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "can limit the query to a subset of clients" do
|
|
20
|
+
set_up_environment(ports_start_at: 10210)
|
|
21
|
+
|
|
22
|
+
cli = Pantry::CLI.new(
|
|
23
|
+
["-a", "chatbot", "status"],
|
|
24
|
+
identity: "test_client"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
client3 = Pantry::Client.new(application: "chatbot", identity: "client3")
|
|
28
|
+
client3.run
|
|
29
|
+
|
|
30
|
+
client4 = Pantry::Client.new(application: "chatbot", identity: "client4")
|
|
31
|
+
client4.run
|
|
32
|
+
|
|
33
|
+
cli.run
|
|
34
|
+
|
|
35
|
+
assert_equal 2, stdout.split("\n").length
|
|
36
|
+
|
|
37
|
+
assert_match /client3/, stdout, "Did not contain line for client3"
|
|
38
|
+
assert_match /client4/, stdout, "Did not contain line for client4"
|
|
39
|
+
|
|
40
|
+
client3.shutdown
|
|
41
|
+
client4.shutdown
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "Client requests information from the Server" do
|
|
4
|
+
|
|
5
|
+
it "asks the server for information and waits for a response" do
|
|
6
|
+
set_up_environment(ports_start_at: 10300)
|
|
7
|
+
|
|
8
|
+
message = ServerEchoCommand.new("Hello Server").to_message
|
|
9
|
+
response_future = @client1.send_request(message)
|
|
10
|
+
|
|
11
|
+
assert_equal ["Hello Server"], response_future.value(2).body
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "handles multiple requests in the proper order" do
|
|
15
|
+
set_up_environment(ports_start_at: 10310)
|
|
16
|
+
|
|
17
|
+
futures = []
|
|
18
|
+
10.times do |i|
|
|
19
|
+
message = ServerEchoCommand.new("Hello Server #{i}").to_message
|
|
20
|
+
futures << @client1.send_request(message)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
futures.each_with_index do |future, idx|
|
|
24
|
+
assert_equal ["Hello Server #{idx}"], future.value(1).body
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "Client / Server heartbeats" do
|
|
4
|
+
|
|
5
|
+
describe "Client" do
|
|
6
|
+
it "re-registers with the server every interval seconds" do
|
|
7
|
+
set_up_environment(ports_start_at: 10500, heartbeat: 1)
|
|
8
|
+
|
|
9
|
+
# Clean out the server registry then wait for clients to re-register themselves
|
|
10
|
+
@server.client_registry.clear!
|
|
11
|
+
|
|
12
|
+
sleep 2
|
|
13
|
+
|
|
14
|
+
assert @server.client_registry.include?(@client1), "Client1 did not check in again"
|
|
15
|
+
assert @server.client_registry.include?(@client2), "Client2 did not check in again"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "Pub/Sub Communication" do
|
|
4
|
+
|
|
5
|
+
describe "Server" do
|
|
6
|
+
it "can publish a message to all connected clients" do
|
|
7
|
+
set_up_environment(ports_start_at: 10400)
|
|
8
|
+
|
|
9
|
+
@server.publish_message(
|
|
10
|
+
Pantry::Message.new("test_message"),
|
|
11
|
+
Pantry::Communication::ClientFilter.new(application: "pantry")
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Give communication time to happen
|
|
15
|
+
sleep 1
|
|
16
|
+
|
|
17
|
+
assert_equal "test_message", @client1.last_received_message.type
|
|
18
|
+
assert_equal "test_message", @client2.last_received_message.type
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "can publish a message to a subset of all connected clients" do
|
|
22
|
+
set_up_environment(ports_start_at: 10410)
|
|
23
|
+
|
|
24
|
+
client3 = Pantry::Client.new(roles: %w(database), identity: "client3")
|
|
25
|
+
client3.run
|
|
26
|
+
|
|
27
|
+
client4 = Pantry::Client.new(roles: %w(database task), identity: "client4")
|
|
28
|
+
client4.run
|
|
29
|
+
|
|
30
|
+
sleep 1
|
|
31
|
+
|
|
32
|
+
@server.publish_message(Pantry::Message.new("to_databases"),
|
|
33
|
+
Pantry::Communication::ClientFilter.new(roles: %w(database)))
|
|
34
|
+
|
|
35
|
+
# Give communication time to happen
|
|
36
|
+
sleep 1
|
|
37
|
+
|
|
38
|
+
assert_equal "to_databases", client3.last_received_message.type
|
|
39
|
+
assert_equal "to_databases", client4.last_received_message.type
|
|
40
|
+
|
|
41
|
+
@server.publish_message(Pantry::Message.new("to_tasks"),
|
|
42
|
+
Pantry::Communication::ClientFilter.new(roles: %w(task)))
|
|
43
|
+
|
|
44
|
+
# Give communication time to happen
|
|
45
|
+
sleep 1
|
|
46
|
+
|
|
47
|
+
assert_equal "to_tasks", client4.last_received_message.type
|
|
48
|
+
|
|
49
|
+
client3.shutdown
|
|
50
|
+
client4.shutdown
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'acceptance/test_helper'
|
|
2
|
+
|
|
3
|
+
describe "ZMQ4 CURVE security" do
|
|
4
|
+
|
|
5
|
+
break unless Pantry::Communication::Security.curve_supported?
|
|
6
|
+
|
|
7
|
+
def set_up_encrypted(ports_start_at, options = {})
|
|
8
|
+
Celluloid.boot
|
|
9
|
+
configure_pantry(ports_start_at: ports_start_at, security: "curve")
|
|
10
|
+
|
|
11
|
+
server_public, server_private = ZMQ::Util.curve_keypair
|
|
12
|
+
client_public, client_private = ZMQ::Util.curve_keypair
|
|
13
|
+
|
|
14
|
+
known_clients = options[:known_clients] || [client_public]
|
|
15
|
+
|
|
16
|
+
key_dir = Pantry.root.join("security", "curve")
|
|
17
|
+
FileUtils.mkdir_p(key_dir)
|
|
18
|
+
|
|
19
|
+
File.open(key_dir.join("server_keys.yml"), "w+") do |f|
|
|
20
|
+
f.write(YAML.dump({
|
|
21
|
+
"private_key" => server_private,
|
|
22
|
+
"public_key" => options[:server_public_key] || server_public,
|
|
23
|
+
"client_keys" => known_clients
|
|
24
|
+
}))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
File.open(key_dir.join("client_keys.yml"), "w+") do |f|
|
|
28
|
+
f.write(YAML.dump({
|
|
29
|
+
"private_key" => client_private, "public_key" => client_public,
|
|
30
|
+
"server_public_key" => options[:server_public_key] || server_public
|
|
31
|
+
}))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "connectivity" do
|
|
36
|
+
def assert_message_timeout(client)
|
|
37
|
+
message = ServerEchoCommand.new("Hello Server").to_message
|
|
38
|
+
response_future = client.send_request(message)
|
|
39
|
+
|
|
40
|
+
assert_raises(Celluloid::TimeoutError) do
|
|
41
|
+
response_future.value(1).body
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assert_successful_message(client)
|
|
46
|
+
message = ServerEchoCommand.new("Hello Server").to_message
|
|
47
|
+
response_future = client.send_request(message)
|
|
48
|
+
|
|
49
|
+
assert_equal ["Hello Server"], response_future.value(2).body
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "configures CURVE security for encrypted server/client communication" do
|
|
53
|
+
set_up_encrypted(15000)
|
|
54
|
+
|
|
55
|
+
server = Pantry::Server.new
|
|
56
|
+
server.identity = "Encrypted Server"
|
|
57
|
+
server.run
|
|
58
|
+
|
|
59
|
+
client = Pantry::Client.new identity: "encrypted-client"
|
|
60
|
+
client.run
|
|
61
|
+
|
|
62
|
+
assert_successful_message(client)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "rejects clients who connect with the wrong server key" do
|
|
66
|
+
set_up_encrypted(15010, server_public_key: "invalid security token1234567890")
|
|
67
|
+
|
|
68
|
+
server = Pantry::Server.new
|
|
69
|
+
server.identity = "Encrypted Server"
|
|
70
|
+
server.run
|
|
71
|
+
|
|
72
|
+
client = Pantry::Client.new identity: "encrypted-client"
|
|
73
|
+
client.run
|
|
74
|
+
|
|
75
|
+
assert_message_timeout(client)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "rejects a client whos public key is not known by the server" do
|
|
79
|
+
set_up_encrypted(15020, known_clients: ["some other client here1234567890"])
|
|
80
|
+
|
|
81
|
+
server = Pantry::Server.new
|
|
82
|
+
server.identity = "Encrypted Server"
|
|
83
|
+
server.run
|
|
84
|
+
|
|
85
|
+
client = Pantry::Client.new identity: "encrypted-client"
|
|
86
|
+
client.run
|
|
87
|
+
|
|
88
|
+
assert_message_timeout(client)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
describe "requesting new client keys" do
|
|
94
|
+
|
|
95
|
+
mock_ui!
|
|
96
|
+
|
|
97
|
+
it "returns a new set of client keys as requested" do
|
|
98
|
+
set_up_encrypted(15030)
|
|
99
|
+
|
|
100
|
+
server = Pantry::Server.new
|
|
101
|
+
server.identity = "Encrypted Server"
|
|
102
|
+
server.run
|
|
103
|
+
|
|
104
|
+
cli = Pantry::CLI.new(
|
|
105
|
+
["client:create"],
|
|
106
|
+
identity: "encrypted-client"
|
|
107
|
+
)
|
|
108
|
+
cli.run
|
|
109
|
+
|
|
110
|
+
assert_match /server_public_key/, stdout
|
|
111
|
+
assert_match /public_key/, stdout
|
|
112
|
+
assert_match /private_key/, stdout
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|