right_agent 0.14.0 → 0.16.2

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