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,79 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::CommandHandler do
4
+
5
+ let(:client) { Pantry::Client.new(identity: "Test Client") }
6
+ let(:command_handler) { Pantry::CommandHandler.new(client) }
7
+
8
+ class TestMessage < Pantry::Command
9
+ def perform(message)
10
+ "Test message ran #{message.uuid}"
11
+ end
12
+ end
13
+
14
+ it "executes commands that match the message type" do
15
+ message = Pantry::Message.new("TestMessage")
16
+
17
+ command_handler.add_command(TestMessage)
18
+ output = command_handler.process(message)
19
+
20
+ assert_equal "Test message ran #{message.uuid}", output
21
+ end
22
+
23
+ it "ignores messages that don't match any command" do
24
+ message = Pantry::Message.new("message_type")
25
+ assert_nil command_handler.process(message)
26
+ end
27
+
28
+ it "knows if it can process a given command or not" do
29
+ command_handler.add_command(TestMessage)
30
+
31
+ message = Pantry::Message.new("unknown_type")
32
+ assert_false command_handler.can_handle?(message), "Should not be able to handle unknown_type"
33
+
34
+ message = Pantry::Message.new("TestMessage")
35
+ assert command_handler.can_handle?(message), "Should be able to handle TestMessage"
36
+ end
37
+
38
+ class ReturnClientIdentity < Pantry::Command
39
+ def perform(message)
40
+ self.client.identity
41
+ end
42
+ end
43
+
44
+ it "sets the server or client on the command before it's performed" do
45
+ message = Pantry::Message.new("ReturnClientIdentity")
46
+
47
+ command_handler.add_command(ReturnClientIdentity)
48
+ response = command_handler.process(message)
49
+
50
+ assert_equal "Test Client", response
51
+ end
52
+
53
+ class ReturnMessageIdentity < Pantry::Command
54
+ def perform(message)
55
+ message
56
+ end
57
+ end
58
+
59
+ it "sets the server or client on the command before it's performed" do
60
+ message = Pantry::Message.new("ReturnMessageIdentity")
61
+
62
+ command_handler.add_command(ReturnMessageIdentity)
63
+ response = command_handler.process(message)
64
+
65
+ assert_equal message, response
66
+ end
67
+
68
+ it "can take a list of command classes on construction to handle" do
69
+ handler = Pantry::CommandHandler.new(
70
+ client, [TestMessage, ReturnClientIdentity, ReturnMessageIdentity])
71
+
72
+ assert handler.can_handle?(Pantry::Message.new("TestMessage")),
73
+ "Did not register TestMessage"
74
+ assert handler.can_handle?(Pantry::Message.new("ReturnClientIdentity")),
75
+ "Did not register ReturnClientIdentity"
76
+ assert handler.can_handle?(Pantry::Message.new("ReturnMessageIdentity")),
77
+ "Did not register ReturnMessageIdentity"
78
+ end
79
+ end
@@ -0,0 +1,5 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::CommandLine do
4
+
5
+ end
@@ -0,0 +1,206 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Command do
4
+
5
+ it "creates a message from itself" do
6
+ command = Pantry::Command.new
7
+ message = command.to_message
8
+
9
+ assert_equal "Command", message.type
10
+ end
11
+
12
+ it "has a link back to the Server or Client handling the command" do
13
+ command = Pantry::Command.new
14
+ command.server_or_client = "client"
15
+
16
+ assert_equal "client", command.server
17
+ assert_equal "client", command.client
18
+ end
19
+
20
+ it "can prepare itself as a Message to be sent down the pipe" do
21
+ command = Pantry::Command.new
22
+ message = command.prepare_message({})
23
+
24
+ assert message.is_a?(Pantry::Message),
25
+ "prepare_message returned the wrong value"
26
+ end
27
+
28
+ describe "Response Handling" do
29
+ describe "#receive_server_response" do
30
+ class ServerCommand < Pantry::Command
31
+ attr_reader :server_response
32
+ def receive_server_response(response)
33
+ @server_response = response
34
+ end
35
+ end
36
+
37
+ before do
38
+ @message = Pantry::Message.new
39
+ @message.from = Pantry::SERVER_IDENTITY
40
+
41
+ @command = ServerCommand.new
42
+ @command.receive_response(@message)
43
+ end
44
+
45
+ it "is triggered by a non-client-list server message" do
46
+ assert_equal @message, @command.server_response,
47
+ "Did not triger the server response handler"
48
+ end
49
+
50
+ it "marks the command as finished" do
51
+ assert @command.finished?
52
+ end
53
+ end
54
+
55
+ describe "#receive_client_response" do
56
+ let(:server_message) do
57
+ Pantry::Message.new.tap do |sm|
58
+ sm.from = Pantry::SERVER_IDENTITY
59
+ sm[:client_response_list] = true
60
+ sm << "client1"
61
+ sm << "client2"
62
+ end
63
+ end
64
+
65
+ class ClientCommand < Pantry::Command
66
+ attr_reader :client_responses
67
+ def receive_client_response(response)
68
+ @client_responses ||= []
69
+ @client_responses << response
70
+ end
71
+ end
72
+
73
+ it "does not forward Server client_list message" do
74
+ command = ClientCommand.new
75
+ command.receive_response(server_message)
76
+
77
+ assert_nil command.client_responses,
78
+ "Should not have forwarded server client list message"
79
+ end
80
+
81
+ it "is triggered by Client responses" do
82
+ client_message = Pantry::Message.new
83
+ client_message.from = "client1"
84
+
85
+ command = ClientCommand.new
86
+ command.receive_response(client_message)
87
+
88
+ assert_equal [client_message], command.client_responses,
89
+ "Did not forward the client message to the handler"
90
+ end
91
+
92
+ it "does not mark the command as finished" do
93
+ client_message = Pantry::Message.new
94
+ client_message.from = "client1"
95
+
96
+ command = ClientCommand.new
97
+ command.receive_response(client_message)
98
+
99
+ assert !command.finished?, "Command was improperly marked as finished"
100
+ end
101
+
102
+ it "marks the command as finished if all Clients have responded" do
103
+ c1 = Pantry::Message.new
104
+ c1.from = "client1"
105
+ c2 = Pantry::Message.new
106
+ c2.from = "client2"
107
+
108
+ command = ClientCommand.new
109
+ command.receive_response(server_message)
110
+ command.receive_response(c1)
111
+ command.receive_response(c2)
112
+
113
+ assert_equal [c1, c2], command.client_responses
114
+ assert command.finished?, "Command was not marked as finished after all responses"
115
+ end
116
+ end
117
+ end
118
+
119
+ module Pantry
120
+ module Commands
121
+ class SubCommand < Pantry::Command
122
+ end
123
+ end
124
+ end
125
+
126
+ module Pantry
127
+ module MyStuff
128
+ class SubCommand < Pantry::Command
129
+ end
130
+ end
131
+ end
132
+
133
+ it "cleans up any known Pantry scoping when figuring out message type" do
134
+ assert_equal "SubCommand", Pantry::Commands::SubCommand.message_type
135
+ assert_equal "MyStuff::SubCommand", Pantry::MyStuff::SubCommand.message_type
136
+ end
137
+
138
+ module John
139
+ module Pete
140
+ class InnerClass < Pantry::Command
141
+ end
142
+ end
143
+ end
144
+
145
+ it "uses the full scoped name of the class" do
146
+ command = John::Pete::InnerClass.new
147
+ message = command.to_message
148
+
149
+ assert_equal "John::Pete::InnerClass", message.type
150
+ end
151
+
152
+ class CustomNameCommand < Pantry::Command
153
+ def self.message_type
154
+ "Gir::WantsWaffles"
155
+ end
156
+ end
157
+
158
+ it "allows custom command types" do
159
+ command = CustomNameCommand.new
160
+ message = command.to_message
161
+
162
+ assert_equal "Gir::WantsWaffles", message.type
163
+ end
164
+
165
+ describe "#send_request!" do
166
+ it "can send a request out and wait for the response" do
167
+ client = Pantry::Client.new
168
+ command = Pantry::Command.new
169
+ command.client = client
170
+ message = Pantry::Message.new
171
+ response = Pantry::Message.new
172
+
173
+ client.expects(:send_request).with(message).returns(mock(:value => response))
174
+
175
+ assert_equal response, command.send_request!(message)
176
+ end
177
+ end
178
+
179
+ describe "#send_request" do
180
+ it "can send a request and return the future for async waiting" do
181
+ client = Pantry::Client.new
182
+ command = Pantry::Command.new
183
+ command.client = client
184
+ message = Pantry::Message.new
185
+
186
+ client.expects(:send_request).with(message).returns("future")
187
+
188
+ assert_equal "future", command.send_request(message)
189
+ end
190
+ end
191
+
192
+ describe "CLI" do
193
+
194
+ class CLICommand < Pantry::Command
195
+ command "cli" do
196
+ description "Sloppy"
197
+ end
198
+ end
199
+
200
+ it "can configure CLI options and information" do
201
+ assert_equal "cli", CLICommand.command_name
202
+ assert_not_nil CLICommand.command_config, "No command config found"
203
+ end
204
+
205
+ end
206
+ end
@@ -0,0 +1,25 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Commands::CreateClient do
4
+
5
+ mock_ui!
6
+
7
+ it "asks server for a new set of encryption keys" do
8
+ server = Pantry::Server.new
9
+
10
+ command = Pantry::Commands::CreateClient.new
11
+ command.server = server
12
+ server.expects(:create_client).returns(
13
+ server_public_key: "server public key",
14
+ public_key: "client public",
15
+ private_key: "client private"
16
+ )
17
+
18
+ keys = command.perform(Pantry::Message.new)
19
+
20
+ assert_equal "server public key", keys[:server_public_key]
21
+ assert_equal "client public", keys[:public_key]
22
+ assert_equal "client private", keys[:private_key]
23
+ end
24
+
25
+ end
@@ -0,0 +1,58 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Commands::DownloadDirectory do
4
+
5
+ fake_fs!
6
+
7
+ it "builds a message with the given directory" do
8
+ message = Pantry::Commands::DownloadDirectory.new("path/here").to_message
9
+ assert_equal "path/here", message.body[0]
10
+ end
11
+
12
+ it "returns filename and contents of all files inside of the requested directory" do
13
+ root_dir = Pantry
14
+ message = Pantry::Message.new
15
+ message << "copy/from"
16
+
17
+ FileUtils.mkdir_p(Pantry.root.join("copy", "from"))
18
+ FileUtils.touch(Pantry.root.join("copy", "from", "app.rb"))
19
+ FileUtils.touch(Pantry.root.join("copy", "from", "db.rb"))
20
+
21
+ command = Pantry::Commands::DownloadDirectory.new
22
+ response = command.perform(message)
23
+
24
+ assert_equal 2, response.length
25
+ assert_equal ["app.rb", ""], response[0]
26
+ assert_equal ["db.rb", ""], response[1]
27
+ end
28
+
29
+ it "does nested read of the given directory" do
30
+ root_dir = Pantry
31
+ message = Pantry::Message.new
32
+ message << "copy/from"
33
+
34
+ FileUtils.mkdir_p(Pantry.root.join("copy", "from", "here", "there"))
35
+ FileUtils.touch(Pantry.root.join("copy", "from", "here", "there", "app.rb"))
36
+ FileUtils.touch(Pantry.root.join("copy", "from", "here", "there", "db.rb"))
37
+
38
+ command = Pantry::Commands::DownloadDirectory.new
39
+ response = command.perform(message)
40
+
41
+ assert_equal 2, response.length
42
+ assert_equal ["here/there/app.rb", ""], response[0]
43
+ assert_equal ["here/there/db.rb", ""], response[1]
44
+ end
45
+
46
+ it "does not allow path traversal attacks" do
47
+ root_dir = Pantry
48
+ message = Pantry::Message.new
49
+ message << "../../../../etc/"
50
+
51
+ command = Pantry::Commands::DownloadDirectory.new
52
+ response = command.perform(message)
53
+
54
+ assert_equal 0, response.length
55
+ end
56
+
57
+ end
58
+
@@ -0,0 +1,22 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Commands::Echo do
4
+
5
+ it "returns the body of the message received" do
6
+ message = Pantry::Message.new("Echo")
7
+ message << "This is a body"
8
+
9
+ command = Pantry::Commands::Echo.new
10
+ results = command.perform(message)
11
+
12
+ assert_equal "This is a body", results
13
+ end
14
+
15
+ it "creates a message with the requested string" do
16
+ command = Pantry::Commands::Echo.new("Hello World")
17
+ message = command.to_message
18
+
19
+ assert_equal "Hello World", message.body[0]
20
+ end
21
+
22
+ end
@@ -0,0 +1,84 @@
1
+ require 'unit/test_helper'
2
+
3
+ describe Pantry::Commands::EditApplication do
4
+ let(:command) { Pantry::Commands::EditApplication.new }
5
+
6
+ fake_fs!
7
+
8
+ describe "#prepare_message" do
9
+ it "requires an application" do
10
+ assert_raises(Pantry::MissingOption) do
11
+ command.prepare_message({})
12
+ end
13
+ end
14
+
15
+ it "puts the application requested in the message" do
16
+ message = command.prepare_message({application: "pantry"})
17
+ assert_equal "pantry", message.body[0]
18
+ end
19
+ end
20
+
21
+ describe "#perform" do
22
+ let(:edit_message) do
23
+ Pantry::Message.new.tap do |msg|
24
+ msg << "pantry"
25
+ end
26
+ end
27
+
28
+ it "returns the configuration of the given application" do
29
+ config_file = Pantry.root.join("applications", "pantry", "config.yml")
30
+ FileUtils.mkdir_p(File.dirname(config_file))
31
+ File.open(config_file, "w+") do |f|
32
+ f.write({"some" => "config"}.to_yaml)
33
+ end
34
+
35
+ response = command.perform(edit_message)
36
+ assert_equal "---\nsome: config\n", response[0]
37
+ end
38
+
39
+ it "returns a new YAML config if no file exist for the application" do
40
+ response = command.perform(edit_message)
41
+ assert_equal "---\nname: pantry\n", response[0]
42
+ end
43
+ end
44
+
45
+ describe "#receive_response" do
46
+ it "lets the user edit the file and uploads when successful" do
47
+ orig_config_body = {name: "pantry"}.to_yaml
48
+ new_config_body = {name: "pantry", config: false}.to_yaml
49
+
50
+ response = Pantry::Message.new
51
+ response.from = Pantry::SERVER_IDENTITY
52
+ response << orig_config_body
53
+
54
+ Pantry::FileEditor.any_instance.expects(:edit).
55
+ with(orig_config_body, :yaml).returns(new_config_body)
56
+
57
+ command.expects(:send_request!).with do |message|
58
+ assert_equal "pantry", message.body[0]
59
+ assert_equal new_config_body, message.body[1]
60
+ end
61
+
62
+ command.prepare_message({application: "pantry"})
63
+ command.receive_response(response)
64
+
65
+ assert command.finished?, "Command was not finished"
66
+ end
67
+
68
+ it "doesn't send the Update command if file contents did not change" do
69
+ config_body = {name: "pantry"}.to_yaml
70
+
71
+ response = Pantry::Message.new
72
+ response.from = Pantry::SERVER_IDENTITY
73
+ response << config_body
74
+
75
+ Pantry::FileEditor.any_instance.expects(:edit).returns(config_body)
76
+
77
+ command.expects(:send_request!).never
78
+
79
+ command.prepare_message({application: "pantry"})
80
+ command.receive_response(response)
81
+ end
82
+ end
83
+
84
+ end