smith 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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: