smith 0.7.9 → 0.8.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 672819de30ca51cefb95f9b58f975574bb59b24a
4
- data.tar.gz: bbc895d5842922f23e866086c549010bb0ec58f1
3
+ metadata.gz: 7fd13c0ca6d5c956ef70e273ea19e61462eeb7f3
4
+ data.tar.gz: 84d57417643b928c8f82a4cb8f6e1a502276fbb5
5
5
  SHA512:
6
- metadata.gz: 2c87facd4414ed52f8380f718ec7585567bb850e1f9928cdc8462ace844575eae1610eaf7164d302f22a36cce7819f0912959e505a9a09d9f3ac85a83a65a5ad
7
- data.tar.gz: 944f322a51b2b8c90c606238565c513e64503024277a3bcf2d6513cc2869ce039ffda9a4dc65b081579858ac9e1b7cd7437eff13400edbfaac503d7dbbe85977
6
+ metadata.gz: c901e07b496c3ae7e1b1c9daf15a0be08364d2fd8b3c3fdde81b7d99729317c6baff917d09cef73917c45ce4371a2085c8015eb4b9e8a7d3614bfb8eae1af5fb
7
+ data.tar.gz: 1aeb3abe83daa143aea4af316fc0d3646f175d1fc586ce3b78318332a073193996b04c297a66aec4fa10f56748f06241f24a9d49d7e7183e8e57ed46c125218d
data/bin/agency CHANGED
@@ -78,7 +78,7 @@ module Smith
78
78
 
79
79
  def add_vhost(name)
80
80
  suffix = Pathname.new(Smith.config.amqp.broker.vhost).basename
81
- "%s.%s" % [name, (suffix.root?) ? 'root' : suffix.to_s]
81
+ "%s.%s" % [name, (suffix.to_s.empty? || suffix.root?) ? 'default' : suffix.to_s]
82
82
  end
83
83
  end
84
84
  end
data/config/smithrc.toml CHANGED
@@ -48,9 +48,11 @@ ack = true
48
48
  [amqp.broker]
49
49
  host = "localhost"
50
50
  port = 5672
51
- user = "guest"
52
- password = "guest"
53
- vhost = "/"
51
+ user = "smith"
52
+ password = "smith"
53
+ vhost = "smith"
54
+
55
+ # uri = "amqp://smith:smith@basking:5672/smith"
54
56
 
55
57
  [logging]
56
58
  trace = true
data/lib/smith.rb CHANGED
@@ -90,11 +90,12 @@ module Smith
90
90
  EM.kqueue
91
91
  end
92
92
 
93
- connection_settings = config.amqp.broker.merge(
93
+ connection_settings = {
94
94
  :on_tcp_connection_failure => method(:tcp_connection_failure_handler),
95
- :on_possible_authentication_failure => method(:authentication_failure_handler))
95
+ :on_possible_authentication_failure => method(:authentication_failure_handler)
96
+ }
96
97
 
97
- AMQP.start(connection_settings) do |connection|
98
+ AMQP.start(config.amqp.broker.uri.to_s, connection_settings) do |connection|
98
99
  @connection = connection
99
100
 
100
101
  connection.on_connection do
@@ -181,7 +182,7 @@ module Smith
181
182
 
182
183
  def broker_identifier(connection)
183
184
  broker = connection.broker.properties
184
- "#{connection.broker_endpoint}, (#{broker['product']}/v#{broker['version']})"
185
+ "amqp://#{connection.broker_endpoint}, (#{broker['product']}/v#{broker['version']}, #{broker['cluster_name']})"
185
186
  end
186
187
 
187
188
  def check_path(path, create=false)
data/lib/smith/agent.rb CHANGED
@@ -56,8 +56,6 @@ module Smith
56
56
  end
57
57
  end
58
58
 
59
- EM.threadpool_size = 1
60
-
61
59
  @on_starting.call(@on_starting_completion)
62
60
  end
63
61
 
@@ -18,7 +18,12 @@ module Smith
18
18
  (a.empty?) ? AgentProcess.new(nil, :name => agent_name, :uuid => nil_uuid) : a.first
19
19
  end
20
20
  when options[:group]
21
- selected_agents = agents.find_by_name(agent_group(options[:group]))
21
+ begin
22
+ selected_agents = agents.find_by_name(agent_group(options[:group]))
23
+ rescue RuntimeError => e
24
+ responder.fail(e.message)
25
+ return
26
+ end
22
27
  else
23
28
  selected_agents = (options[:all]) ? agents.to_a : agents.state(:running)
24
29
  end
@@ -13,6 +13,9 @@ module Smith
13
13
  # @return [Array<Class>] the class of each agent in the group
14
14
  def agent_group(group)
15
15
  agents = Smith.agent_directories.map do |agent_directory|
16
+
17
+ raise RuntimeError, "No group specified." unless group
18
+
16
19
  group_directory(agent_directory, group) do |group_directory, groups_prefix|
17
20
 
18
21
  if group_directory.exist? && group_directory.directory?
data/lib/smith/config.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  require 'toml'
5
5
  require 'fileutils'
6
6
  require 'pathname'
7
+ require 'addressable/uri'
7
8
  require 'hashie/extensions/coercion'
8
9
  require 'hashie/extensions/deep_merge'
9
10
  require 'hashie/extensions/method_access'
@@ -53,6 +54,7 @@ module Smith
53
54
  @config_file = find_config_file
54
55
  @config = load_tomls(default_config_file, @config_file)
55
56
  coerce_directories!
57
+ broker_uri!
56
58
  end
57
59
 
58
60
  # Make sure the non-default direcotires are set.
@@ -81,6 +83,24 @@ module Smith
81
83
  @config.agency[:acl_directories] = @config.agency[:acl_directories] + [smith_acl_directory]
82
84
  end
83
85
 
86
+ def broker_uri!
87
+ uri = if @config.amqp.broker[:uri]
88
+ Addressable::URI.parse(@config.amqp.broker[:uri])
89
+ else
90
+ Addressable::URI.new.tap do |u|
91
+ u.scheme = 'amqp'
92
+ u.host = @config.amqp.broker.delete(:host)
93
+ u.port = @config.amqp.broker.delete(:port)
94
+ u.path = @config.amqp.broker.delete(:vhost)
95
+ u.user = @config.amqp.broker.delete(:user)
96
+ u.password = @config.amqp.broker.delete(:password)
97
+ end
98
+ end
99
+
100
+ @config.amqp.broker[:uri] = uri_from_env('SMITH_BROKER_URI', uri)
101
+ @config.amqp.broker[:vhost] = @config.amqp.broker[:uri].path
102
+ end
103
+
84
104
  # Find the config file. This checks the following paths before raising an
85
105
  # exception:
86
106
  #
@@ -122,7 +142,7 @@ module Smith
122
142
  to_pathname(ENV.fetch(env_var, default))
123
143
  end
124
144
 
125
- # Returns an array of path from the environment variable passed in. If the
145
+ # Returns an array of Paths from the environment variable passed in. If the
126
146
  # environment variable is not set it returns an empty array.
127
147
  #
128
148
  # @param env_var [String] the name of the environment variable
@@ -132,6 +152,16 @@ module Smith
132
152
  split_paths(ENV.fetch(env_var, default))
133
153
  end
134
154
 
155
+ # Returns a URI from the environment variable passed in. If the
156
+ # environment variable is not set it returns nil.
157
+ #
158
+ # @param env_var [String] the name of the environment variable
159
+ # @param default [String] the value to use if the environment variable is not set
160
+ # @return [Addressable::URI]
161
+ def uri_from_env(env_var, default)
162
+ Addressable::URI.parse(ENV.fetch(env_var, default))
163
+ end
164
+
135
165
  # Splits a string using PATH_SEPARATOR.
136
166
  #
137
167
  # @param paths to split
@@ -16,5 +16,6 @@ module Smith
16
16
  class Error < RuntimeError; end
17
17
  class IncorrectTypeError < Error; end
18
18
  class UnknownError < Error; end
19
+ class UnknownTypeFormat < Error; end
19
20
  end
20
21
  end
@@ -10,68 +10,119 @@ module Smith
10
10
  include Singleton
11
11
  include MurmurHash3
12
12
 
13
+ SUPPORTED_FORMATS = [:string, :binary]
14
+ DEFAULT_FORMAT = :string
15
+
13
16
  def initialize
14
17
  clear!
15
18
  end
16
19
 
20
+ # Add the type to the cashe. This will add the type for all know formats
21
+ # @param type [Class] the type to add
22
+ # @return [true|false] true if the type was added or false if it already exists.
17
23
  def add(type)
18
- if @types[type]
24
+ if SUPPORTED_FORMATS.all? { |format| @types[format].has_key?(type) }
19
25
  false
20
26
  else
21
- h = to_murmur32(type)
22
- @types[type] = h
23
- @hashes[h] = type
27
+ SUPPORTED_FORMATS.each do |format|
28
+ h = to_murmur32(type, format)
29
+ @types[format][type] = h
30
+ @hashes[format][h] = type
31
+ end
32
+
24
33
  @legacy_types_by_hash[type.to_s.split(/::/)[-1].snake_case] = type
25
34
  true
26
35
  end
27
36
  end
28
37
 
29
- def get_by_hash(type)
30
- if @hashes[type]
31
- @hashes[type]
32
- elsif @legacy_types_by_hash[type.to_s]
33
- @legacy_types_by_hash[type.to_s]
38
+ # Return the type given the mumur3 hash
39
+ # @param type [Sting] the mumur3 hash to lookup
40
+ # @param format [Symbol] the format of the mumor3 hash. Defaults to
41
+ # Smith::AclTypeCache::DEFAULT_FORMAT
42
+ # @return [Class]
43
+ # @raise [Smith::ACL::UnknownError] raised when an unknown ACL is given
44
+ def get_by_hash(type, format=DEFAULT_FORMAT)
45
+ if t = dump_hashes(format)[type]
46
+ t
47
+ else
48
+ if t = @legacy_types_by_hash[type.to_s]
49
+ t
50
+ else
51
+ raise ACL::UnknownError, "Unknown ACL: #{t}"
52
+ end
34
53
  end
35
54
  end
36
55
 
37
- def get_by_type(type)
38
- @types[type]
56
+ # Return the mumur3 hash of the given the type
57
+ # @param type [Class] the class to lookup
58
+ # @param format [Symbol] the format of the mumor3 hash. Defaults to
59
+ # Smith::AclTypeCache::DEFAULT_FORMAT
60
+ # @return [String]
61
+ def get_by_type(type, format=DEFAULT_FORMAT)
62
+ dump_types(format)[type].tap { |t| raise ACL::UnknownError, "Unknown ACL: #{t}" if type.nil? }
39
63
  end
40
64
 
41
65
  # Look the key up in the cache. This defaults to the key being the hash.
42
66
  # If :by_type => true is passed in as the second argument then it will
43
67
  # perform the lookup in the type hash.
44
- #
45
68
  def include?(key, opts={})
46
69
  if opts[:by_type]
47
- !get_by_type(key).nil?
70
+ !get_by_type(key, opts.fetch(:format, DEFAULT_FORMAT)).nil?
48
71
  else
49
- !get_by_hash(key).nil?
72
+ !get_by_hash(key, opts.fetch(:format, DEFAULT_FORMAT)).nil?
50
73
  end
51
74
  end
52
75
 
53
76
  # Clear the internal hashes.
54
77
  def clear!
55
- @types = {}
56
- @hashes = {}
78
+ @types = SUPPORTED_FORMATS.each_with_object({}) { |v, acc| acc[v] = {} }
79
+ @hashes = SUPPORTED_FORMATS.each_with_object({}) { |v, acc| acc[v] = {} }
57
80
  @legacy_types_by_hash = {}
58
81
  end
59
82
 
60
83
  # Dump the type hash
61
- def dump_types
62
- @types
84
+ # @param format [Symbol] the format of the mumor3 hash. Defaults to
85
+ # Smith::AclTypeCache::DEFAULT_FORMAT
86
+ # @return [Hash]
87
+ # @raise [Smith::ACL::UnknownTypeFormat] raised when an unknown format is given
88
+ def dump_types(format=DEFAULT_FORMAT)
89
+ if @types.has_key?(format)
90
+ @types[format]
91
+ else
92
+ raise ACL::UnknownTypeFormat, "Uknown format: #{format}"
93
+ end
63
94
  end
64
95
 
65
96
  # Dump the hashes hash
66
- def dump_hashes
67
- @hashes
97
+ # @param format [Symbol] the format of the mumor3 hash. Defaults to
98
+ # Smith::AclTypeCache::DEFAULT_FORMAT
99
+ # @return [Hash]
100
+ # @raise [Smith::ACL::UnknownTypeFormat] raised when an unknown format is given
101
+ def dump_hashes(format=DEFAULT_FORMAT)
102
+ if @hashes.has_key?(format)
103
+ @hashes[format]
104
+ else
105
+ raise ACL::UnknownTypeFormat, "Uknown format: #{format}"
106
+ end
68
107
  end
69
108
 
70
109
  private
71
110
 
72
- # Convert the name to a base 36 murmur hash
73
- def to_murmur32(type)
74
- V32.murmur3_32_str_hash(type.to_s).to_s(36)
111
+ # Convert the type to a murmor3 hash
112
+ # @param type [Class] the class to lookup
113
+ # @param format [Symbol] the format of the mumor3 hash. Defaults to
114
+ # Smith::AclTypeCache::DEFAULT_FORMAT
115
+ # @return [String]
116
+ # @raise [Smith::ACL::UnknownTypeFormat] raised when an unknown format is given
117
+ def to_murmur32(type, format)
118
+ case format
119
+ when :string
120
+ MurmurHash3::V32.murmur3_32_str_hash(type.to_s).to_s(36)
121
+ when :binary
122
+ MurmurHash3::V32.murmur3_32_str_hash(type.to_s)
123
+ else
124
+ raise ACL::UnknownTypeFormat, "Uknown format: #{format}"
125
+ end
75
126
  end
76
127
  end
77
128
  end
@@ -21,6 +21,10 @@ module Smith
21
21
  :auto_ack => option_or_default(@queue_def.options, :auto_ack, true),
22
22
  :threading => option_or_default(@queue_def.options, :threading, false)}
23
23
 
24
+ fanout_options = if opts.delete(:fanout)
25
+ {:persistence => opts.delete(:fanout_persistence) { true }, :queue_suffix => opts.delete(:fanout_queue_suffix)}
26
+ end
27
+
24
28
  @payload_type = Array(option_or_default(@queue_def.options, :type, []))
25
29
 
26
30
  prefetch = option_or_default(@queue_def.options, :prefetch, Smith.config.agent.prefetch)
@@ -39,13 +43,14 @@ module Smith
39
43
 
40
44
  open_channel(:prefetch => prefetch) do |channel|
41
45
  @channel_completion.succeed(channel)
42
- channel.direct(@queue_def.normalise, @options.exchange) do |exchange|
46
+ exchange_type = (fanout_options) ? :fanout : :direct
47
+ channel.send(exchange_type, @queue_def.normalise, @options.exchange) do |exchange|
43
48
  @exchange_completion.succeed(exchange)
44
49
  end
45
50
  end
46
51
 
47
52
  open_channel(:prefetch => prefetch) do |channel|
48
- channel.queue(@queue_def.normalise, @options.queue) do |queue|
53
+ channel.queue(*fanout_queue_and_opts(fanout_options)) do |queue|
49
54
  @exchange_completion.completion do |exchange|
50
55
  queue.bind(exchange, :routing_key => @queue_def.normalise)
51
56
  @queue_completion.succeed(queue)
@@ -68,9 +73,9 @@ module Smith
68
73
  @exchange_completion.completion do |exchange|
69
74
  logger.debug { "Attaching to reply queue: #{reply_queue_name}" }
70
75
 
71
- queue_def = QueueDefinition.new(reply_queue_name, :auto_delete => true, :immediate => true, :mandatory => true, :durable => false)
76
+ reply_queue_def = QueueDefinition.new(reply_queue_name, :auto_delete => true, :immediate => true, :mandatory => true, :durable => false)
72
77
 
73
- Smith::Messaging::Sender.new(queue_def) do |sender|
78
+ Smith::Messaging::Sender.new(reply_queue_def) do |sender|
74
79
  @reply_queues[reply_queue_name] = sender
75
80
  blk.call(sender)
76
81
  end
@@ -200,6 +205,33 @@ module Smith
200
205
  requeue_options.merge!(:on_requeue_limit => blk)
201
206
  end
202
207
  end
208
+
209
+ private
210
+
211
+ # Calculates the queue name and the various queue options based on the
212
+ # fanout_options and returns them in a form that can be used by the
213
+ # queue constuctor.
214
+ #
215
+ # @param [Hash] fanout_options the various fanout related options
216
+ #
217
+ # @raise [ArgumentError] raised if :queue_suffix is given without the :persistence flag
218
+ #
219
+ # @return [Array<queue_name, options]
220
+ def fanout_queue_and_opts(fanout_options)
221
+ if fanout_options
222
+ if fanout_options[:persistence]
223
+ if fanout_options[:queue_suffix]
224
+ ["#{@queue_def.normalise}.#{fanout_options[:queue_suffix]}", @options.queue]
225
+ else
226
+ raise ArgumentError, "Incorrect options. :fanout_queue_suffix must be provided if :fanout_persistence is true."
227
+ end
228
+ else
229
+ ["", @options.queue.merge(:durable => false, :auto_delete => true)]
230
+ end
231
+ else
232
+ [@queue_def.normalise, @options.queue]
233
+ end
234
+ end
203
235
  end
204
236
 
205
237
  class Foo
@@ -218,7 +250,6 @@ module Smith
218
250
 
219
251
  # TODO add some better error checking/diagnostics.
220
252
  clazz = @acl_type_cache.get_by_hash(metadata.type)
221
- raise ACL::UnknownError, "Unknown ACL: #{metadata.type}" if clazz.nil?
222
253
 
223
254
  @message = clazz.new.parse_from_string(data)
224
255
 
@@ -29,18 +29,26 @@ module Smith
29
29
 
30
30
  open_channel(:prefetch => prefetch) do |channel|
31
31
  @channel_completion.succeed(channel)
32
- channel.direct(@queue_def.normalise, @options.exchange) do |exchange|
32
+ exchange_type = (opts[:fanout]) ? :fanout : :direct
33
33
 
34
- exchange.on_return do |basic_return,metadata,payload|
35
- logger.error { "#{@acl_type_cache[metadata.type].new.parse_from_string} returned! Exchange: #{reply_code.exchange}, reply_code: #{basic_return.reply_code}, reply_text: #{basic_return.reply_text}" }
36
- logger.error { "Properties: #{metadata.properties}" }
37
- end
34
+ channel.send(exchange_type, @queue_def.normalise, @options.exchange) do |exchange|
38
35
 
39
- channel.queue(@queue_def.normalise, @options.queue) do |queue|
40
- queue.bind(exchange, :routing_key => @queue_def.normalise)
36
+ exchange.on_return do |basic_return, metadata, payload|
37
+ logger.error {
38
+ acl_type = @acl_type_cache.get_by_hash(metadata.properties[:type]).to_s rescue "Unknown ACL type"
39
+ "Cannot deliver message type: #{acl_type}, from exchange: \"#{basic_return.exchange}\", using routing key: \"#{basic_return.routing_key}\""
40
+ }
41
+ end
41
42
 
42
- @queue_completion.succeed(queue)
43
+ if opts[:fanout]
43
44
  @exchange_completion.succeed(exchange)
45
+ else
46
+ channel.queue(@queue_def.normalise, @options.queue) do |queue|
47
+ queue.bind(exchange, :routing_key => @queue_def.normalise)
48
+
49
+ @queue_completion.succeed(queue)
50
+ @exchange_completion.succeed(exchange)
51
+ end
44
52
  end
45
53
  end
46
54
  end
data/lib/smith/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Smith
2
- VERSION = "0.7.9"
2
+ VERSION = "0.8.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.9
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Heycock
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-12 00:00:00.000000000 Z
11
+ date: 2016-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: amqp
@@ -30,6 +30,20 @@ dependencies:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '1.5'
33
+ - !ruby/object:Gem::Dependency
34
+ name: addressable
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
33
47
  - !ruby/object:Gem::Dependency
34
48
  name: daemons
35
49
  requirement: !ruby/object:Gem::Requirement
@@ -346,9 +360,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
346
360
  version: '0'
347
361
  requirements: []
348
362
  rubyforge_project: smith
349
- rubygems_version: 2.4.5
363
+ rubygems_version: 2.5.1
350
364
  signing_key:
351
365
  specification_version: 4
352
366
  summary: Multi-agent framework
353
367
  test_files: []
354
- has_rdoc: