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.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +19 -0
  5. data/Gemfile +15 -0
  6. data/Guardfile +16 -0
  7. data/LICENSE +20 -0
  8. data/README.md +53 -0
  9. data/Rakefile +18 -0
  10. data/Vagrantfile +86 -0
  11. data/bin/pantry +11 -0
  12. data/bin/pantry-client +38 -0
  13. data/bin/pantry-server +33 -0
  14. data/dist/client.yml +79 -0
  15. data/dist/server.yml +56 -0
  16. data/dist/upstart/pantry-client.conf +12 -0
  17. data/dist/upstart/pantry-server.conf +12 -0
  18. data/doc/message_packet.dot +19 -0
  19. data/doc/message_packet.dot.png +0 -0
  20. data/doc/network_topology.dot +42 -0
  21. data/doc/network_topology.dot.png +0 -0
  22. data/lib/celluloid_zmq_patches.rb +16 -0
  23. data/lib/opt_parse_plus.rb +184 -0
  24. data/lib/pantry.rb +197 -0
  25. data/lib/pantry/cli.rb +154 -0
  26. data/lib/pantry/client.rb +131 -0
  27. data/lib/pantry/client_info.rb +34 -0
  28. data/lib/pantry/client_registry.rb +104 -0
  29. data/lib/pantry/command.rb +194 -0
  30. data/lib/pantry/command_handler.rb +53 -0
  31. data/lib/pantry/command_line.rb +115 -0
  32. data/lib/pantry/commands/create_client.rb +30 -0
  33. data/lib/pantry/commands/download_directory.rb +35 -0
  34. data/lib/pantry/commands/echo.rb +32 -0
  35. data/lib/pantry/commands/edit_application.rb +60 -0
  36. data/lib/pantry/commands/register_client.rb +38 -0
  37. data/lib/pantry/commands/status.rb +78 -0
  38. data/lib/pantry/commands/sync_directory.rb +50 -0
  39. data/lib/pantry/commands/update_application.rb +45 -0
  40. data/lib/pantry/commands/upload_file.rb +68 -0
  41. data/lib/pantry/communication.rb +20 -0
  42. data/lib/pantry/communication/client.rb +75 -0
  43. data/lib/pantry/communication/client_filter.rb +117 -0
  44. data/lib/pantry/communication/file_service.rb +125 -0
  45. data/lib/pantry/communication/file_service/file_progress.rb +164 -0
  46. data/lib/pantry/communication/file_service/receive_file.rb +97 -0
  47. data/lib/pantry/communication/file_service/send_file.rb +74 -0
  48. data/lib/pantry/communication/publish_socket.rb +20 -0
  49. data/lib/pantry/communication/reading_socket.rb +89 -0
  50. data/lib/pantry/communication/receive_socket.rb +23 -0
  51. data/lib/pantry/communication/security.rb +44 -0
  52. data/lib/pantry/communication/security/authentication.rb +98 -0
  53. data/lib/pantry/communication/security/curve_key_store.rb +120 -0
  54. data/lib/pantry/communication/security/curve_security.rb +70 -0
  55. data/lib/pantry/communication/security/null_security.rb +32 -0
  56. data/lib/pantry/communication/send_socket.rb +19 -0
  57. data/lib/pantry/communication/serialize_message.rb +84 -0
  58. data/lib/pantry/communication/server.rb +97 -0
  59. data/lib/pantry/communication/subscribe_socket.rb +33 -0
  60. data/lib/pantry/communication/wait_list.rb +45 -0
  61. data/lib/pantry/communication/writing_socket.rb +46 -0
  62. data/lib/pantry/config.rb +182 -0
  63. data/lib/pantry/file_editor.rb +67 -0
  64. data/lib/pantry/logger.rb +78 -0
  65. data/lib/pantry/message.rb +134 -0
  66. data/lib/pantry/multi_command.rb +36 -0
  67. data/lib/pantry/server.rb +132 -0
  68. data/lib/pantry/test/acceptance.rb +83 -0
  69. data/lib/pantry/test/support/fake_fs.rb +31 -0
  70. data/lib/pantry/test/support/matchers.rb +13 -0
  71. data/lib/pantry/test/support/minitest.rb +13 -0
  72. data/lib/pantry/test/support/mock_ui.rb +23 -0
  73. data/lib/pantry/test/unit.rb +13 -0
  74. data/lib/pantry/ui.rb +68 -0
  75. data/lib/pantry/version.rb +3 -0
  76. data/pantry.gemspec +40 -0
  77. data/test/acceptance/cli/error_handling_test.rb +7 -0
  78. data/test/acceptance/cli/execute_command_on_clients_test.rb +32 -0
  79. data/test/acceptance/cli/request_info_from_server_test.rb +44 -0
  80. data/test/acceptance/communication/client_requests_info_from_server_test.rb +28 -0
  81. data/test/acceptance/communication/heartbeat_test.rb +19 -0
  82. data/test/acceptance/communication/pub_sub_communication_test.rb +53 -0
  83. data/test/acceptance/communication/security_test.rb +117 -0
  84. data/test/acceptance/communication/server_requests_info_from_client_test.rb +41 -0
  85. data/test/acceptance/test_helper.rb +25 -0
  86. data/test/fixtures/config.yml +22 -0
  87. data/test/fixtures/empty.yml +2 -0
  88. data/test/fixtures/file_to_upload +3 -0
  89. data/test/root_dir/.gitkeep +0 -0
  90. data/test/unit/cli_test.rb +173 -0
  91. data/test/unit/client_registry_test.rb +61 -0
  92. data/test/unit/client_test.rb +128 -0
  93. data/test/unit/command_handler_test.rb +79 -0
  94. data/test/unit/command_line_test.rb +5 -0
  95. data/test/unit/command_test.rb +206 -0
  96. data/test/unit/commands/create_client_test.rb +25 -0
  97. data/test/unit/commands/download_directory_test.rb +58 -0
  98. data/test/unit/commands/echo_test.rb +22 -0
  99. data/test/unit/commands/edit_application_test.rb +84 -0
  100. data/test/unit/commands/register_client_test.rb +41 -0
  101. data/test/unit/commands/status_test.rb +81 -0
  102. data/test/unit/commands/sync_directory_test.rb +75 -0
  103. data/test/unit/commands/update_application_test.rb +35 -0
  104. data/test/unit/commands/upload_file_test.rb +51 -0
  105. data/test/unit/communication/client_filter_test.rb +262 -0
  106. data/test/unit/communication/client_test.rb +99 -0
  107. data/test/unit/communication/file_service/receive_file_test.rb +214 -0
  108. data/test/unit/communication/file_service/send_file_test.rb +110 -0
  109. data/test/unit/communication/file_service_test.rb +56 -0
  110. data/test/unit/communication/publish_socket_test.rb +19 -0
  111. data/test/unit/communication/reading_socket_test.rb +110 -0
  112. data/test/unit/communication/receive_socket_test.rb +20 -0
  113. data/test/unit/communication/security/authentication_test.rb +97 -0
  114. data/test/unit/communication/security/curve_key_store_test.rb +110 -0
  115. data/test/unit/communication/security/curve_security_test.rb +44 -0
  116. data/test/unit/communication/security/null_security_test.rb +15 -0
  117. data/test/unit/communication/security_test.rb +49 -0
  118. data/test/unit/communication/send_socket_test.rb +19 -0
  119. data/test/unit/communication/serialize_message_test.rb +128 -0
  120. data/test/unit/communication/server_test.rb +106 -0
  121. data/test/unit/communication/subscribe_socket_test.rb +46 -0
  122. data/test/unit/communication/wait_list_test.rb +60 -0
  123. data/test/unit/communication/writing_socket_test.rb +46 -0
  124. data/test/unit/config_test.rb +150 -0
  125. data/test/unit/logger_test.rb +79 -0
  126. data/test/unit/message_test.rb +179 -0
  127. data/test/unit/multi_command_test.rb +45 -0
  128. data/test/unit/opt_parse_plus_test.rb +218 -0
  129. data/test/unit/pantry_test.rb +82 -0
  130. data/test/unit/server_test.rb +166 -0
  131. data/test/unit/test_helper.rb +25 -0
  132. data/test/unit/ui_test.rb +58 -0
  133. metadata +389 -13
@@ -0,0 +1,41 @@
1
+ require 'acceptance/test_helper'
2
+
3
+ describe "Server requests info from the Client" do
4
+
5
+ it "asks a client for info and waits for the response" do
6
+ set_up_environment(ports_start_at: 10600)
7
+
8
+ message = Pantry::Commands::Echo.new("Hello Client").to_message
9
+ response_future = @server.send_request(@client1, message)
10
+
11
+ assert_equal ["Hello Client"], response_future.value(1).body
12
+ end
13
+
14
+ it "asks multiple clients for info and matches responses with requests" do
15
+ set_up_environment(ports_start_at: 10610)
16
+
17
+ message1 = Pantry::Commands::Echo.new("Hello Client1").to_message
18
+ message2 = Pantry::Commands::Echo.new("Hello Client2").to_message
19
+
20
+ future1 = @server.send_request(@client1, message1)
21
+ future2 = @server.send_request(@client2, message2)
22
+
23
+ assert_equal ["Hello Client1"], future1.value(1).body
24
+ assert_equal ["Hello Client2"], future2.value(1).body
25
+ end
26
+
27
+ it "handles multiple subsequent requests of the same type to the same client" do
28
+ set_up_environment(ports_start_at: 10620)
29
+
30
+ futures = []
31
+ 10.times do |i|
32
+ message = Pantry::Commands::Echo.new("Hello Client #{i}").to_message
33
+ futures << @server.send_request(@client1, message)
34
+ end
35
+
36
+ 10.times do |i|
37
+ assert_equal ["Hello Client #{i}"], futures[i].value(5).body
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,25 @@
1
+ require 'pantry/test/acceptance'
2
+
3
+ # Swap these lines to turn on the logger in tests
4
+ Pantry.logger.disable!
5
+ #Pantry.config.log_level = :debug
6
+
7
+ class Minitest::Test
8
+
9
+ def setup
10
+ Pantry.config.root_dir = File.expand_path("../../root_dir", __FILE__)
11
+ clean_up_pantry_root
12
+ end
13
+
14
+ # Ensure Pantry.root is always clean for each test.
15
+ def clean_up_pantry_root
16
+ Dir["#{Pantry.root}/**/*"].each do |file|
17
+ FileUtils.rm_rf file
18
+ end
19
+ end
20
+
21
+ def fixture_path(file_path)
22
+ File.join(File.dirname(__FILE__), "..", "fixtures", file_path)
23
+ end
24
+
25
+ end
@@ -0,0 +1,22 @@
1
+ ---
2
+ log_to: /var/log/pantry.log
3
+ log_level: warn
4
+ syslog_program_name: testing
5
+ root_dir: /tmp/data
6
+
7
+ networking:
8
+ server_host: 10.0.0.1
9
+ pub_sub_port: 12345
10
+ receive_port: 54321
11
+ file_service_port: 35412
12
+ security: curve
13
+
14
+ client:
15
+ heartbeat_interval: 600
16
+ identity: "pantry-test-1"
17
+ application: "pantry"
18
+ environment: "test"
19
+ roles:
20
+ - database
21
+ - application
22
+
@@ -0,0 +1,2 @@
1
+ ---
2
+ nothing: here
@@ -0,0 +1,3 @@
1
+ Hello
2
+ Pantry
3
+ !
File without changes
@@ -0,0 +1,173 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::CLI do
4
+
5
+ let(:filter) { Pantry::Communication::ClientFilter.new }
6
+
7
+ mock_ui!
8
+
9
+ def build_cli(command)
10
+ Pantry::CLI.new([command].flatten)
11
+ end
12
+
13
+ it "defaults identity to the current ENV['USER']" do
14
+ assert_equal ENV["USER"], build_cli("echo").identity
15
+ end
16
+
17
+ it "builds a message from a command request and sends it to the server" do
18
+ cli = build_cli("status")
19
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
20
+
21
+ cli.expects(:send_message).with do |message|
22
+ assert_equal "Status", message.type
23
+ end
24
+
25
+ cli.run
26
+ end
27
+
28
+ it "passes along arguments to the command handler" do
29
+ cli = build_cli(["echo", "Hello World"])
30
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
31
+
32
+ cli.expects(:send_message).with do |message|
33
+ assert message.requires_response?, "Message not flagged to require response"
34
+ assert_equal "Echo", message.type
35
+ assert_equal "Hello World", message.body[0]
36
+ end
37
+
38
+ cli.run
39
+ end
40
+
41
+ it "includes global options when passing options to a command handler" do
42
+ cli = build_cli(["-a", "pantry", "-e", "test", "echo", "Hello World"])
43
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
44
+
45
+ Pantry::Commands::Echo.any_instance.expects(:prepare_message).with do |options|
46
+ assert_equal "pantry", options["application"]
47
+ assert_equal "test", options["environment"]
48
+ end.returns(stub_everything)
49
+
50
+ cli.stubs(:send_message)
51
+
52
+ cli.run
53
+ end
54
+
55
+ it "sets the logging level to info on -v" do
56
+ Pantry.config.expects(:refresh)
57
+
58
+ capture_io do
59
+ cli = build_cli(["-v"])
60
+ cli.run
61
+ end
62
+
63
+ assert_equal :info, Pantry.config.log_level
64
+ end
65
+
66
+ it "sets logging level to debug on -d" do
67
+ Pantry.config.expects(:refresh)
68
+
69
+ capture_io do
70
+ cli = build_cli(["-d"])
71
+ cli.run
72
+ end
73
+
74
+ assert_equal :debug, Pantry.config.log_level
75
+ end
76
+
77
+ it "sets the hostname of the server to communicate with" do
78
+ capture_io do
79
+ cli = build_cli(["-h", "localhost"])
80
+ cli.run
81
+ end
82
+
83
+ assert_equal "localhost", Pantry.config.server_host
84
+ end
85
+
86
+ it "prints out the version of pantry when requested" do
87
+ cli = build_cli(["-V"])
88
+ cli.run
89
+
90
+ assert_equal Pantry::VERSION, stdout.strip
91
+ end
92
+
93
+ it "prints the help if nothing given on the command line" do
94
+ out, err = capture_io do
95
+ cli = build_cli([])
96
+ cli.run
97
+ end
98
+
99
+ assert_match /Usage:/, out
100
+ end
101
+
102
+ it "can be given a set of filters to limit the request to a certain subset of clients" do
103
+ cli = build_cli(["-a", "pantry", "-e", "test", "echo", "Hello World"])
104
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
105
+
106
+ cli.expects(:send_message).with do |message|
107
+ assert_equal "Echo", message.type
108
+ assert_equal "pantry.test", message.to
109
+ end
110
+
111
+ cli.run
112
+ end
113
+
114
+ it "reads a local config file and sets default options" do
115
+ File.open(Pantry.root.join("config"), "w+") do |f|
116
+ f.puts("-a pantry")
117
+ f.puts("--environment test")
118
+ f.puts("-r app")
119
+ end
120
+
121
+ cli = build_cli(["echo", "Hello World"])
122
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
123
+
124
+ cli.expects(:send_message).with do |message|
125
+ assert_equal "Echo", message.type
126
+ assert_equal "pantry.test.app", message.to
127
+ end
128
+
129
+ cli.run
130
+ end
131
+
132
+ it "turns on Curve and sets keys if --curve-key-file is set" do
133
+ break unless Pantry::Communication::Security.curve_supported?
134
+ Pantry::Command.any_instance.stubs(:wait_for_finish)
135
+
136
+ File.open(Pantry.root.join("keys.yml"), "w+") do |f|
137
+ f.write(YAML.dump(
138
+ "server_public_key" => "x" * 40,
139
+ "public_key" => "y" * 40,
140
+ "private_key" => "z" * 40
141
+ ))
142
+ end
143
+
144
+ capture_io do
145
+ cli = build_cli(["--curve-key-file", "keys.yml"])
146
+ cli.run
147
+ end
148
+
149
+ assert_equal "curve", Pantry.config.security
150
+
151
+ assert FileUtils.compare_file(
152
+ Pantry.root.join("keys.yml"),
153
+ Pantry.root.join("security", "curve", "client_keys.yml")
154
+ ), "Did not copy the keys file into a client-ready position"
155
+ end
156
+
157
+ it "errors if the curve-key-file is not found"
158
+
159
+ it "forwards messages received to the current command" do
160
+ cli = build_cli("status")
161
+ cli.stubs(:send_message)
162
+
163
+ command = Pantry::Command.new
164
+ cli.async.request(filter, command, {})
165
+
166
+ message = Pantry::Message.new
167
+ message.from = Pantry::SERVER_IDENTITY
168
+
169
+ cli.receive_message(message)
170
+ assert command.finished?, "Command was not marked as finished"
171
+ end
172
+
173
+ end
@@ -0,0 +1,61 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::ClientRegistry do
4
+
5
+ before do
6
+ @c1 = Pantry::ClientInfo.new identity: "client1", application: "pantry", environment: "test"
7
+ @c2 = Pantry::ClientInfo.new identity: "client2", environment: "test"
8
+
9
+ @registry = Pantry::ClientRegistry.new
10
+ @registry.check_in(@c1)
11
+ @registry.check_in(@c2)
12
+ end
13
+
14
+ it "marks a client as checked in" do
15
+ assert @registry.include?(@c1)
16
+ assert @registry.include?(@c2)
17
+ end
18
+
19
+ it "does not duplicate clients in the list from multiple check-ins" do
20
+ @registry.check_in(@c1)
21
+ @registry.check_in(@c1)
22
+ @registry.check_in(@c1)
23
+
24
+ assert_equal [@c1, @c2], @registry.all
25
+ end
26
+
27
+ it "returns all known clients that match the given stream" do
28
+ assert_equal [@c1, @c2], @registry.all_matching("")
29
+ assert_equal [@c1], @registry.all_matching("pantry.test")
30
+ assert_equal [@c1], @registry.all_matching("client1")
31
+ assert_equal [@c2], @registry.all_matching("client2")
32
+ end
33
+
34
+ it "returns all known clients that match a given ClientFilter" do
35
+ assert_equal [@c1, @c2], @registry.all_matching(Pantry::Communication::ClientFilter.new)
36
+
37
+ filter = Pantry::Communication::ClientFilter.new(application: "pantry", environment: "test")
38
+ assert_equal [@c1], @registry.all_matching(filter)
39
+
40
+ filter = Pantry::Communication::ClientFilter.new(environment: "test")
41
+ assert_equal [@c2], @registry.all_matching(filter)
42
+ end
43
+
44
+ it "processes each found client via the block if given" do
45
+ found = @registry.all_matching(Pantry::Communication::ClientFilter.new) do |client, record|
46
+ client.identity
47
+ end
48
+
49
+ assert_equal [@c1.identity, @c2.identity], found
50
+ end
51
+
52
+ it "can return all known clients" do
53
+ assert_equal [@c1, @c2], @registry.all
54
+ end
55
+
56
+ it "returns the client who matches the given identity" do
57
+ assert_equal @c1, @registry.find("client1")
58
+ assert_nil @registry.find("unknown")
59
+ end
60
+
61
+ end
@@ -0,0 +1,128 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Client do
4
+
5
+ class FakeNetworkStack
6
+ def initialize(listener)
7
+ end
8
+
9
+ def self.new_link(listener)
10
+ new(listener)
11
+ end
12
+ end
13
+
14
+ before do
15
+ FakeNetworkStack.any_instance.stubs(:send_message)
16
+ end
17
+
18
+ it "can take a list of roles this Client manages" do
19
+ client = Pantry::Client.new roles: %w(app db)
20
+ assert_equal %w(app db), client.roles
21
+ end
22
+
23
+ it "can take an application to manage" do
24
+ client = Pantry::Client.new application: "pantry"
25
+ assert_equal "pantry", client.application
26
+ end
27
+
28
+ it "can take an environment to manage" do
29
+ client = Pantry::Client.new environment: "production"
30
+ assert_equal "production", client.environment
31
+ end
32
+
33
+ it "packages up all filter information into a Filter object" do
34
+ client = Pantry::Client.new environment: "production", application: "pantry"
35
+
36
+ filter = Pantry::Communication::ClientFilter.new(
37
+ environment: "production", application: "pantry", identity: client.identity)
38
+
39
+ assert_equal filter, client.filter
40
+ assert_equal "production", client.environment
41
+ end
42
+
43
+ it "starts up and shuts down the networking stack" do
44
+ FakeNetworkStack.any_instance.expects(:run)
45
+
46
+ client = Pantry::Client.new(network_stack_class: FakeNetworkStack)
47
+ client.run
48
+ end
49
+
50
+ it "sends a registration packet once networking is up" do
51
+ client = Pantry::Client.new(identity: "johnson", network_stack_class: FakeNetworkStack)
52
+
53
+ FakeNetworkStack.any_instance.stubs(:run)
54
+ FakeNetworkStack.any_instance.expects(:send_message).with do |message|
55
+ assert_equal "RegisterClient", message.type
56
+ end
57
+
58
+ client.run
59
+ end
60
+
61
+ it "executes callbacks when a message matches" do
62
+ client = Pantry::Client.new
63
+
64
+ Pantry::CommandHandler.any_instance.stubs(:can_handle?).returns(true)
65
+ Pantry::CommandHandler.any_instance.expects(:process).with do |message|
66
+ message.type == "test_message"
67
+ end
68
+
69
+ client.receive_message(Pantry::Message.new("test_message"))
70
+ end
71
+
72
+ it "builds and sends a response message if message flagged as needing one" do
73
+ client = Pantry::Client.new(network_stack_class: FakeNetworkStack)
74
+
75
+ Pantry::CommandHandler.any_instance.stubs(:can_handle?).returns(true)
76
+ Pantry::CommandHandler.any_instance.expects(:process).returns("A response message")
77
+
78
+ message = Pantry::Message.new("test_message")
79
+ message.requires_response!
80
+
81
+ FakeNetworkStack.any_instance.expects(:send_message).with do |response_message|
82
+ assert_equal "test_message", response_message.type
83
+ assert_equal ["A response message"], response_message.body
84
+ end
85
+
86
+ client.receive_message(message)
87
+ end
88
+
89
+ it "keeps all object formatting of the response message" do
90
+ client = Pantry::Client.new(network_stack_class: FakeNetworkStack)
91
+
92
+ Pantry::CommandHandler.any_instance.stubs(:can_handle?).returns(true)
93
+ Pantry::CommandHandler.any_instance.expects(:process).returns([%w(A response message)])
94
+
95
+ message = Pantry::Message.new("test_message")
96
+ message.requires_response!
97
+
98
+ FakeNetworkStack.any_instance.expects(:send_message).with do |response_message|
99
+ assert_equal [["A", "response", "message"]], response_message.body
100
+ end
101
+
102
+ client.receive_message(message)
103
+ end
104
+
105
+ it "can request info of a the server" do
106
+ client = Pantry::Client.new(network_stack_class: FakeNetworkStack)
107
+ message = Pantry::Message.new("test message")
108
+
109
+ FakeNetworkStack.any_instance.expects(:send_request).with(message)
110
+
111
+ client.send_request( message)
112
+
113
+ assert message.requires_response?, "Message should require a response from the server"
114
+ end
115
+
116
+ describe "Identity" do
117
+ it "can be given a specific identity" do
118
+ c = Pantry::Client.new identity: "My Test Client"
119
+ assert_equal "My Test Client", c.identity
120
+ end
121
+
122
+ it "defaults to the hostname of the machine" do
123
+ c = Pantry::Client.new
124
+ assert_equal `hostname`.strip, c.identity
125
+ end
126
+ end
127
+
128
+ end