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.
- data/README.rdoc +2 -0
- data/lib/right_agent/actors/agent_manager.rb +1 -1
- data/lib/right_agent/agent.rb +28 -14
- data/lib/right_agent/agent_config.rb +1 -1
- data/lib/right_agent/agent_identity.rb +4 -5
- data/lib/right_agent/agent_tag_manager.rb +21 -24
- data/lib/right_agent/core_payload_types/executable_bundle.rb +1 -1
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +7 -0
- data/lib/right_agent/core_payload_types/right_script_instantiation.rb +20 -5
- data/lib/right_agent/exceptions.rb +44 -1
- data/lib/right_agent/history.rb +4 -1
- data/lib/right_agent/packets.rb +2 -1
- data/lib/right_agent/platform/darwin.rb +6 -0
- data/lib/right_agent/platform/linux.rb +5 -1
- data/lib/right_agent/platform/windows.rb +8 -4
- data/lib/right_agent/scripts/stats_manager.rb +3 -3
- data/lib/right_agent/security/cached_certificate_store_proxy.rb +27 -13
- data/lib/right_agent/security/encrypted_document.rb +1 -2
- data/lib/right_agent/security/static_certificate_store.rb +30 -14
- data/lib/right_agent/sender.rb +101 -47
- data/lib/right_agent/serialize/secure_serializer.rb +29 -27
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +3 -3
- data/lib/right_agent/serialize/serializable.rb +1 -1
- data/lib/right_agent/serialize/serializer.rb +15 -6
- data/right_agent.gemspec +4 -5
- data/spec/agent_spec.rb +2 -2
- data/spec/agent_tag_manager_spec.rb +330 -0
- data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
- data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +14 -8
- data/spec/security/static_certificate_store_spec.rb +13 -7
- data/spec/sender_spec.rb +114 -17
- data/spec/serialize/secure_serializer_spec.rb +78 -49
- data/spec/serialize/serializer_spec.rb +21 -2
- 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
|
-
|
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]
|
data/lib/right_agent/agent.rb
CHANGED
@@ -38,8 +38,8 @@ module RightScale
|
|
38
38
|
# (Hash) Configuration options applied to the agent
|
39
39
|
attr_reader :options
|
40
40
|
|
41
|
-
# (
|
42
|
-
attr_reader :
|
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" =>
|
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
|
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
|
-
@
|
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
|
-
#
|
569
|
-
def
|
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
|
724
|
-
|
725
|
-
|
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
|
@@ -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
|
100
|
+
# (RightScale::Exceptions::Argument):: Serialized agent identity is invalid
|
101
101
|
def self.parse(serialized_id)
|
102
|
-
|
103
|
-
|
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
|
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(
|
72
|
-
|
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
|
-
|
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)::
|
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(
|
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)::
|
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(
|
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)::
|
187
|
-
# agent_ids(Array)::
|
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
|
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.
|
126
|
+
desc = @executables.collect { |e| e.title }.join(', ') if @executables
|
127
127
|
desc ||= 'empty bundle'
|
128
128
|
end
|
129
129
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
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
|
-
# (
|
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
|
-
# (
|
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
|
-
|
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
|
data/lib/right_agent/history.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2009-
|
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
|