right_agent 0.14.0 → 0.16.2

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 (35) hide show
  1. data/README.rdoc +2 -0
  2. data/lib/right_agent/actors/agent_manager.rb +1 -1
  3. data/lib/right_agent/agent.rb +28 -14
  4. data/lib/right_agent/agent_config.rb +1 -1
  5. data/lib/right_agent/agent_identity.rb +4 -5
  6. data/lib/right_agent/agent_tag_manager.rb +21 -24
  7. data/lib/right_agent/core_payload_types/executable_bundle.rb +1 -1
  8. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +7 -0
  9. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +20 -5
  10. data/lib/right_agent/exceptions.rb +44 -1
  11. data/lib/right_agent/history.rb +4 -1
  12. data/lib/right_agent/packets.rb +2 -1
  13. data/lib/right_agent/platform/darwin.rb +6 -0
  14. data/lib/right_agent/platform/linux.rb +5 -1
  15. data/lib/right_agent/platform/windows.rb +8 -4
  16. data/lib/right_agent/scripts/stats_manager.rb +3 -3
  17. data/lib/right_agent/security/cached_certificate_store_proxy.rb +27 -13
  18. data/lib/right_agent/security/encrypted_document.rb +1 -2
  19. data/lib/right_agent/security/static_certificate_store.rb +30 -14
  20. data/lib/right_agent/sender.rb +101 -47
  21. data/lib/right_agent/serialize/secure_serializer.rb +29 -27
  22. data/lib/right_agent/serialize/secure_serializer_initializer.rb +3 -3
  23. data/lib/right_agent/serialize/serializable.rb +1 -1
  24. data/lib/right_agent/serialize/serializer.rb +15 -6
  25. data/right_agent.gemspec +4 -5
  26. data/spec/agent_spec.rb +2 -2
  27. data/spec/agent_tag_manager_spec.rb +330 -0
  28. data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
  29. data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
  30. data/spec/security/cached_certificate_store_proxy_spec.rb +14 -8
  31. data/spec/security/static_certificate_store_spec.rb +13 -7
  32. data/spec/sender_spec.rb +114 -17
  33. data/spec/serialize/secure_serializer_spec.rb +78 -49
  34. data/spec/serialize/serializer_spec.rb +21 -2
  35. metadata +90 -36
data/README.rdoc CHANGED
@@ -20,6 +20,8 @@ documentation.
20
20
  Also use the built-in issues tracker (https://github.com/rightscale/right_agent/issues)
21
21
  to report issues.
22
22
 
23
+ Maintained by the RightScale Teal Team
24
+
23
25
  == Interface
24
26
 
25
27
  A RightAgent exposes its services via actors and methods that are invoked by requests
@@ -81,7 +81,7 @@ class AgentManager
81
81
  # === Return
82
82
  # (OperationResult):: Empty success result or error result with message
83
83
  def profile(options)
84
- require 'memprof'
84
+ return error_result("The memprof gem is not available for profiling. Please install memprof 0.3 manually") unless require_succeeds?('memprof')
85
85
 
86
86
  options = RightScale::SerializationHelper.symbolize_keys(options || {})
87
87
  if options[:start]
@@ -38,8 +38,8 @@ module RightScale
38
38
  # (Hash) Configuration options applied to the agent
39
39
  attr_reader :options
40
40
 
41
- # (Dispatcher) Dispatcher for messages received
42
- attr_reader :dispatcher
41
+ # (Hash) Dispatcher for each queue for messages received
42
+ attr_reader :dispatchers
43
43
 
44
44
  # (ActorRegistry) Registry for this agents actors
45
45
  attr_reader :registry
@@ -373,7 +373,7 @@ module RightScale
373
373
  def terminate(reason = nil, exception = nil, &block)
374
374
  block ||= DEFAULT_TERMINATE_BLOCK
375
375
  begin
376
- @history.update("stop")
376
+ @history.update("stop") if @history
377
377
  Log.error("[stop] Terminating because #{reason}", exception, :trace) if reason
378
378
  if @terminating || @broker.nil?
379
379
  @terminating = true
@@ -381,7 +381,7 @@ module RightScale
381
381
  @termination_timer = nil
382
382
  Log.info("[stop] Terminating immediately")
383
383
  block.call
384
- @history.update("graceful exit") if @broker.nil?
384
+ @history.update("graceful exit") if @history && @broker.nil?
385
385
  else
386
386
  @terminating = true
387
387
  @check_status_timer.cancel if @check_status_timer
@@ -417,7 +417,7 @@ module RightScale
417
417
  "version" => AgentConfig.protocol_version,
418
418
  "brokers" => @broker.stats(reset),
419
419
  "agent stats" => agent_stats(reset),
420
- "receive stats" => @dispatcher.stats(reset),
420
+ "receive stats" => dispatcher_stats(reset),
421
421
  "send stats" => @sender.stats(reset),
422
422
  "last reset time" => @last_stat_reset_time.to_i,
423
423
  "stat time" => now.to_i,
@@ -463,7 +463,7 @@ module RightScale
463
463
  stats
464
464
  end
465
465
 
466
- # Reset cache statistics
466
+ # Reset agent statistics
467
467
  #
468
468
  # === Return
469
469
  # true:: Always return true
@@ -476,6 +476,14 @@ module RightScale
476
476
  true
477
477
  end
478
478
 
479
+ # Get dispatcher statistics
480
+ #
481
+ # === Return
482
+ # (Hash):: Current statistics
483
+ def dispatcher_stats(reset)
484
+ @dispatchers[@identity].stats(reset)
485
+ end
486
+
479
487
  # Set the agent's configuration using the supplied options
480
488
  #
481
489
  # === Parameters
@@ -539,7 +547,7 @@ module RightScale
539
547
  def start_service(&terminate_callback)
540
548
  begin
541
549
  @registry = ActorRegistry.new
542
- @dispatcher = create_dispatcher
550
+ @dispatchers = create_dispatchers
543
551
  @sender = create_sender
544
552
  load_actors
545
553
  setup_traps
@@ -562,13 +570,14 @@ module RightScale
562
570
  true
563
571
  end
564
572
 
565
- # Create dispatcher for handling incoming requests
573
+ # Create dispatcher per queue for use in handling incoming requests
566
574
  #
567
575
  # === Return
568
- # (Dispatcher):: New dispatcher
569
- def create_dispatcher
576
+ # [Hash]:: Dispatchers with queue name as key
577
+ def create_dispatchers
570
578
  cache = DispatchedCache.new(@identity) if @options[:dup_check]
571
- Dispatcher.new(self, cache)
579
+ dispatcher = Dispatcher.new(self, cache)
580
+ @queues.inject({}) { |dispatchers, queue| dispatchers[queue] = dispatcher; dispatchers }
572
581
  end
573
582
 
574
583
  # Create manager for outgoing requests
@@ -720,9 +729,14 @@ module RightScale
720
729
  # true:: Always return true
721
730
  def dispatch_request(request, queue)
722
731
  begin
723
- if result = @dispatcher.dispatch(request)
724
- exchange = {:type => :queue, :name => "response", :options => {:durable => true, :no_declare => @options[:secure]}}
725
- @broker.publish(exchange, result, :persistent => true, :mandatory => true, :log_filter => [:tries, :persistent, :duration])
732
+ if (dispatcher = @dispatchers[queue])
733
+ if (result = dispatcher.dispatch(request))
734
+ exchange = {:type => :queue, :name => request.reply_to, :options => {:durable => true, :no_declare => @options[:secure]}}
735
+ @broker.publish(exchange, result, :persistent => true, :mandatory => true, :log_filter => [:request_from, :tries, :persistent, :duration])
736
+ end
737
+ else
738
+ Log.error("Failed to dispatch request #{request.trace} from queue #{queue} because no dispatcher configured")
739
+ @request_failure_stats.update("NoConfiguredDispatcher")
726
740
  end
727
741
  rescue Dispatcher::DuplicateRequest
728
742
  rescue RightAMQP::HABrokerClient::NoConnectedBrokers => e
@@ -63,7 +63,7 @@ module RightScale
63
63
  module AgentConfig
64
64
 
65
65
  # Current agent protocol version
66
- PROTOCOL_VERSION = 20
66
+ PROTOCOL_VERSION = 21
67
67
 
68
68
  # Current agent protocol version
69
69
  #
@@ -97,13 +97,12 @@ module RightScale
97
97
  # (AgentIdentity):: Corresponding agent identity
98
98
  #
99
99
  # === Raise
100
- # (RightScale::Exceptions::Argument):: Serialized agent identity is incorrect
100
+ # (RightScale::Exceptions::Argument):: Serialized agent identity is invalid
101
101
  def self.parse(serialized_id)
102
- serialized_id = self.compatible_serialized(serialized_id)
103
- prefix, agent_type, token, bid, separator = parts(serialized_id)
104
- raise RightScale::Exceptions::Argument, "Invalid agent identity token" unless prefix && agent_type && token && bid
102
+ prefix, agent_type, token, bid, separator = parts(self.compatible_serialized(serialized_id))
103
+ raise RightScale::Exceptions::Argument, "Invalid agent identity: #{serialized_id.inspect}" unless prefix && agent_type && token && bid
105
104
  base_id = bid.to_i
106
- raise RightScale::Exceptions::Argument, "Invalid agent identity token (Base ID)" unless base_id.to_s == bid
105
+ raise RightScale::Exceptions::Argument, "Invalid agent identity base ID: #{bid ? bid : bid.inspect}" unless base_id.to_s == bid
107
106
 
108
107
  AgentIdentity.new(prefix, agent_type, base_id, token, separator)
109
108
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2009 RightScale Inc
2
+ # Copyright (c) 2009-2013 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -20,7 +20,6 @@
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
-
24
23
  module RightScale
25
24
 
26
25
  # Agent tags management
@@ -43,7 +42,7 @@ module RightScale
43
42
  #
44
43
  # === Return
45
44
  # true:: Always return true
46
- def tags(options={})
45
+ def tags(options = {})
47
46
  do_query(nil, @agent.identity, options) do |result|
48
47
  if result.kind_of?(Hash)
49
48
  yield(result.size == 1 ? result.values.first['tags'] : [])
@@ -57,7 +56,7 @@ module RightScale
57
56
  # of the given tags.
58
57
  #
59
58
  # === Parameters
60
- # tags(Array):: tags to query or empty
59
+ # tags(String, Array):: Tag or tags to query or empty
61
60
  # options(Hash):: Request options
62
61
  # :raw(Boolean):: true to yield raw tag response instead of deserialized tags
63
62
  # :timeout(Integer):: timeout in seconds before giving up and yielding an error message
@@ -68,14 +67,8 @@ module RightScale
68
67
  #
69
68
  # === Return
70
69
  # true:: Always return true
71
- def query_tags(*tags)
72
- if tags.last.respond_to?(:keys)
73
- tags = tags[0..-2]
74
- options = tags.last
75
- else
76
- options = {}
77
- end
78
-
70
+ def query_tags(tags, options = {})
71
+ tags = ensure_flat_array_value(tags) unless tags.nil? || tags.empty?
79
72
  do_query(tags, nil, options) { |result| yield result }
80
73
  end
81
74
 
@@ -83,7 +76,7 @@ module RightScale
83
76
  # of the given tags. Yields the raw response (for responding locally).
84
77
  #
85
78
  # === Parameters
86
- # tags(Array):: tags to query or empty
79
+ # tags(String, Array):: Tag or tags to query or empty
87
80
  # agent_ids(Array):: agent IDs to query or empty or nil
88
81
  # options(Hash):: Request options
89
82
  # :timeout(Integer):: timeout in seconds before giving up and yielding an error message
@@ -93,15 +86,16 @@ module RightScale
93
86
  #
94
87
  # === Return
95
88
  # true:: Always return true
96
- def query_tags_raw(tags, agent_ids = nil, options={})
97
- options = options.merge(:raw=>true)
89
+ def query_tags_raw(tags, agent_ids = nil, options = {})
90
+ tags = ensure_flat_array_value(tags) unless tags.nil? || tags.empty?
91
+ options = options.merge(:raw => true)
98
92
  do_query(tags, agent_ids, options) { |raw_response| yield raw_response }
99
93
  end
100
94
 
101
95
  # Add given tags to agent
102
96
  #
103
97
  # === Parameters
104
- # new_tags(Array):: Tags to be added
98
+ # new_tags(String, Array):: Tag or tags to be added
105
99
  #
106
100
  # === Block
107
101
  # A block is optional. If provided, should take one argument which will be set with the
@@ -109,14 +103,15 @@ module RightScale
109
103
  #
110
104
  # === Return
111
105
  # true always return true
112
- def add_tags(*new_tags)
106
+ def add_tags(new_tags)
107
+ new_tags = ensure_flat_array_value(new_tags) unless new_tags.nil? || new_tags.empty?
113
108
  update_tags(new_tags, []) { |raw_response| yield raw_response if block_given? }
114
109
  end
115
110
 
116
111
  # Remove given tags from agent
117
112
  #
118
113
  # === Parameters
119
- # old_tags(Array):: Tags to be removed
114
+ # old_tags(String, Array):: Tag or tags to be removed
120
115
  #
121
116
  # === Block
122
117
  # A block is optional. If provided, should take one argument which will be set with the
@@ -124,7 +119,8 @@ module RightScale
124
119
  #
125
120
  # === Return
126
121
  # true always return true
127
- def remove_tags(*old_tags)
122
+ def remove_tags(old_tags)
123
+ old_tags = ensure_flat_array_value(old_tags) unless old_tags.nil? || old_tags.empty?
128
124
  update_tags([], old_tags) { |raw_response| yield raw_response if block_given? }
129
125
  end
130
126
 
@@ -171,7 +167,7 @@ module RightScale
171
167
  # === Return
172
168
  # true::Always return true
173
169
  def clear
174
- update_tags([], tags) { |raw_response| yield raw_response }
170
+ update_tags([], @agent.tags) { |raw_response| yield raw_response }
175
171
  end
176
172
 
177
173
  private
@@ -183,10 +179,10 @@ module RightScale
183
179
  # Runs a tag query with an optional list of tags.
184
180
  #
185
181
  # === Parameters
186
- # tags(Array):: tags to query or empty or nil
187
- # agent_ids(Array):: agent IDs to query or empty or nil
182
+ # tags(Array):: Tags to query or empty or nil
183
+ # agent_ids(Array):: IDs of agents to query with empty or nil meaning all agents in deployment
188
184
  # options(Hash):: Request options
189
- # :raw(Boolean):: true to yield raw tag response instead of deserialized tags
185
+ # :raw(Boolean):: true to yield raw tag response instead of unserialized tags
190
186
  # :timeout(Integer):: timeout in seconds before giving up and yielding an error message
191
187
  #
192
188
  # === Block
@@ -195,7 +191,7 @@ module RightScale
195
191
  #
196
192
  # === Return
197
193
  # true:: Always return true
198
- def do_query(tags = nil, agent_ids = nil, options={})
194
+ def do_query(tags = nil, agent_ids = nil, options = {})
199
195
  raw = options[:raw] || false
200
196
  timeout = options[:timeout]
201
197
 
@@ -241,4 +237,5 @@ module RightScale
241
237
  # to the old typename
242
238
  # TODO remove this alias for RightAgent 1.0
243
239
  AgentTagsManager = AgentTagManager
240
+
244
241
  end # RightScale
@@ -123,7 +123,7 @@ module RightScale
123
123
  # === Return
124
124
  # desc(String):: Auditable description
125
125
  def to_s
126
- desc = @executables.collect { |e| e.nickname }.join(', ') if @executables
126
+ desc = @executables.collect { |e| e.title }.join(', ') if @executables
127
127
  desc ||= 'empty bundle'
128
128
  end
129
129
  end
@@ -62,5 +62,12 @@ module RightScale
62
62
  [ @nickname, @attributes, @id, @ready, @external_inputs, @input_flags ]
63
63
  end
64
64
 
65
+ # Human readable title
66
+ #
67
+ # === Return
68
+ # @return [String] title for display
69
+ def title
70
+ nickname
71
+ end
65
72
  end
66
73
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2009-2011 RightScale Inc
2
+ # Copyright (c) 2009-2013 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -41,7 +41,7 @@ module RightScale
41
41
  # (Array) RightScript attachments URLs, array of RightScriptAttachment
42
42
  attr_accessor :attachments
43
43
 
44
- # (Array) RightScripts required packages
44
+ # (String) RightScripts required packages as a space-delimited list of package names or empty
45
45
  attr_accessor :packages
46
46
 
47
47
  # (Integer) RightScript id
@@ -50,7 +50,7 @@ module RightScale
50
50
  # (Boolean) Whether script inputs are ready
51
51
  attr_accessor :ready
52
52
 
53
- # (Array of SecureDocumentLocation) attributes that must be resolved by the instance
53
+ # (Hash) a map of input names to CredentialLocations which must be retrieved by the instance or nil or empty
54
54
  attr_accessor :external_inputs
55
55
 
56
56
  # (Hash) nil or Hash of input name to flags (array of string tokens) indicating additional
@@ -58,6 +58,9 @@ module RightScale
58
58
  # flag means true, absense means false.
59
59
  attr_accessor :input_flags
60
60
 
61
+ # (String) Displayable version for RightScript (revision, etc.) or nil
62
+ attr_accessor :display_version
63
+
61
64
  def initialize(*args)
62
65
  @nickname = args[0] if args.size > 0
63
66
  @source = args[1] if args.size > 1
@@ -68,12 +71,24 @@ module RightScale
68
71
  @ready = args[6] if args.size > 6
69
72
  @external_inputs = args[7] if args.size > 7
70
73
  @input_flags = args[8] if args.size > 8
74
+ @display_version = args[9] if args.size > 9
71
75
  end
72
76
 
73
77
  # Array of serialized fields given to constructor
74
78
  def serialized_members
75
- [ @nickname, @source, @parameters, @attachments, @packages, @id, @ready, @external_inputs, @input_flags ]
79
+ [ @nickname, @source, @parameters, @attachments, @packages, @id, @ready, @external_inputs, @input_flags, @display_version ]
80
+ end
81
+
82
+ # Human readable title
83
+ #
84
+ # === Return
85
+ # @return [String] title for display
86
+ def title
87
+ if @display_version
88
+ "'#{nickname}' #{@display_version}"
89
+ else
90
+ nickname
91
+ end
76
92
  end
77
-
78
93
  end
79
94
  end
@@ -22,9 +22,52 @@
22
22
 
23
23
  module RightScale
24
24
  class Exceptions
25
- class Application < RuntimeError; end
25
+ # Capability not currently supported
26
+ class NotSupported < Exception; end
27
+
28
+ # Internal application error
29
+ class Application < RuntimeError
30
+ attr_reader :nested_exception
31
+ def initialize(message, nested_exception = nil)
32
+ @nested_exception = nested_exception
33
+ super(message)
34
+ end
35
+ end
36
+
37
+ # Invalid command or method argument
26
38
  class Argument < RuntimeError; end
39
+
40
+ # Agent command IO error
27
41
  class IO < RuntimeError; end
42
+
43
+ # Agent compute platform error
28
44
  class PlatformError < StandardError; end
45
+
46
+ # Cannot connect or lost connection to external resource
47
+ class ConnectivityFailure < RuntimeError
48
+ attr_reader :nested_exception
49
+ def initialize(message, nested_exception = nil)
50
+ @nested_exception = nested_exception
51
+ super(message)
52
+ end
53
+ end
54
+
55
+ # Request failed but potentially will succeed if retried
56
+ class RetryableError < RuntimeError
57
+ attr_reader :nested_exception
58
+ def initialize(message, nested_exception = nil)
59
+ @nested_exception = nested_exception
60
+ super(message)
61
+ end
62
+ end
63
+
64
+ # Database query failed
65
+ class QueryFailure < RuntimeError
66
+ attr_reader :nested_exception
67
+ def initialize(message, nested_exception = nil)
68
+ @nested_exception = nested_exception
69
+ super(message)
70
+ end
71
+ end
29
72
  end
30
73
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2009-2011 RightScale Inc
2
+ # Copyright (c) 2009-2012 RightScale Inc
3
3
  #
4
4
  # Permission is hereby granted, free of charge, to any person obtaining
5
5
  # a copy of this software and associated documentation files (the
@@ -20,6 +20,8 @@
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
+ require 'fileutils'
24
+
23
25
  module RightScale
24
26
 
25
27
  # Agent history manager
@@ -44,6 +46,7 @@ module RightScale
44
46
  # true:: Always return true
45
47
  def update(event)
46
48
  @last_update = {:time => Time.now.to_i, :pid => @pid, :event => event}
49
+ FileUtils.mkdir_p(File.dirname(@history)) unless File.exists?(File.dirname(@history))
47
50
  File.open(@history, "a") { |f| f.puts @last_update.to_json }
48
51
  true
49
52
  end