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.
- 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
@@ -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
|
@@ -26,12 +26,13 @@ module RightScale
|
|
26
26
|
# X.509 certificate signing
|
27
27
|
class SecureSerializer
|
28
28
|
|
29
|
+
class MissingPrivateKey < Exception; end
|
29
30
|
class MissingCertificate < Exception; end
|
30
31
|
class InvalidSignature < Exception; end
|
31
32
|
|
32
|
-
#
|
33
|
-
def self.init(serializer, identity,
|
34
|
-
@serializer = SecureSerializer.new(serializer, identity,
|
33
|
+
# Create the one and only SecureSerializer
|
34
|
+
def self.init(serializer, identity, store, encrypt = true)
|
35
|
+
@serializer = SecureSerializer.new(serializer, identity, store, encrypt)
|
35
36
|
true
|
36
37
|
end
|
37
38
|
|
@@ -40,14 +41,16 @@ module RightScale
|
|
40
41
|
!@serializer.nil?
|
41
42
|
end
|
42
43
|
|
43
|
-
#
|
44
|
+
# See SecureSerializer#dump
|
44
45
|
def self.dump(obj, encrypt = nil)
|
46
|
+
raise "Secure serializer not initialized" unless initialized?
|
45
47
|
@serializer.dump(obj, encrypt)
|
46
48
|
end
|
47
49
|
|
48
|
-
#
|
49
|
-
def self.load(msg)
|
50
|
-
|
50
|
+
# See SecureSerializer#load
|
51
|
+
def self.load(msg, id = nil)
|
52
|
+
raise "Secure serializer not initialized" unless initialized?
|
53
|
+
@serializer.load(msg, id)
|
51
54
|
end
|
52
55
|
|
53
56
|
# Initialize serializer, must be called prior to using it
|
@@ -55,17 +58,19 @@ module RightScale
|
|
55
58
|
# === Parameters
|
56
59
|
# serializer(Serializer):: Object serializer
|
57
60
|
# identity(String):: Serialized identity associated with serialized messages
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# encryption (get_recipients) and signature validation (get_signer)
|
61
|
+
# store(Object):: Credentials store exposing certificates used for
|
62
|
+
# encryption (:get_target), signature validation (:get_signer), and
|
63
|
+
# certificate(s)/key(s) used for decryption (:get_receiver)
|
62
64
|
# encrypt(Boolean):: true if data should be signed and encrypted, otherwise
|
63
65
|
# just signed, true by default
|
64
|
-
def initialize(serializer, identity,
|
66
|
+
def initialize(serializer, identity, store, encrypt = true)
|
65
67
|
@identity = identity
|
66
|
-
|
67
|
-
@key = key
|
68
|
+
raise "Missing local agent identity" unless @identity
|
68
69
|
@store = store
|
70
|
+
raise "Missing credentials store" unless @store
|
71
|
+
@cert, @key = @store.get_receiver(@identity)
|
72
|
+
raise "Missing local agent public certificate" unless @cert
|
73
|
+
raise "Missing local agent private key" unless @key
|
69
74
|
@encrypt = encrypt
|
70
75
|
@serializer = serializer
|
71
76
|
end
|
@@ -84,10 +89,6 @@ module RightScale
|
|
84
89
|
# === Raise
|
85
90
|
# Exception:: If certificate identity, certificate store, certificate, or private key missing
|
86
91
|
def dump(obj, encrypt = nil)
|
87
|
-
raise "Missing certificate identity" unless @identity
|
88
|
-
raise "Missing certificate" unless @cert
|
89
|
-
raise "Missing certificate key" unless @key
|
90
|
-
raise "Missing certificate store" unless @store || !@encrypt
|
91
92
|
must_encrypt = encrypt || @encrypt
|
92
93
|
serialize_format = if obj.respond_to?(:send_version) && obj.send_version >= 12
|
93
94
|
@serializer.format
|
@@ -97,12 +98,12 @@ module RightScale
|
|
97
98
|
encode_format = serialize_format == :json ? :pem : :der
|
98
99
|
msg = @serializer.dump(obj, serialize_format)
|
99
100
|
if must_encrypt
|
100
|
-
certs = @store.
|
101
|
+
certs = @store.get_target(obj)
|
101
102
|
if certs
|
102
103
|
msg = EncryptedDocument.new(msg, certs).encrypted_data(encode_format)
|
103
104
|
else
|
104
105
|
target = obj.target_for_encryption if obj.respond_to?(:target_for_encryption)
|
105
|
-
Log.
|
106
|
+
Log.error("No certs available for object #{obj.class} being sent to #{target.inspect}\n") if target
|
106
107
|
end
|
107
108
|
end
|
108
109
|
sig = Signature.new(msg, @cert, @key).data(encode_format)
|
@@ -114,6 +115,8 @@ module RightScale
|
|
114
115
|
#
|
115
116
|
# === Parameters
|
116
117
|
# msg(String):: Serialized and optionally encrypted object using MessagePack or JSON
|
118
|
+
# id(String|nil):: Optional identifier of source of data for use
|
119
|
+
# in determining who is the receiver
|
117
120
|
#
|
118
121
|
# === Return
|
119
122
|
# (Object):: Unserialized object
|
@@ -122,11 +125,7 @@ module RightScale
|
|
122
125
|
# Exception:: If certificate store, certificate, or private key missing
|
123
126
|
# MissingCertificate:: If could not find certificate for message signer
|
124
127
|
# InvalidSignature:: If message signature check failed for message
|
125
|
-
def load(msg)
|
126
|
-
raise "Missing certificate store" unless @store
|
127
|
-
raise "Missing certificate" unless @cert || !@encrypt
|
128
|
-
raise "Missing certificate key" unless @key || !@encrypt
|
129
|
-
|
128
|
+
def load(msg, id = nil)
|
130
129
|
msg = @serializer.load(msg)
|
131
130
|
sig = Signature.from_data(msg['signature'])
|
132
131
|
certs = @store.get_signer(msg['id'])
|
@@ -137,7 +136,10 @@ module RightScale
|
|
137
136
|
|
138
137
|
data = msg['data']
|
139
138
|
if data && msg['encrypted']
|
140
|
-
|
139
|
+
cert, key = @store.get_receiver(id)
|
140
|
+
raise MissingCertificate.new("Could not find a certificate for #{id.inspect}") unless cert
|
141
|
+
raise MissingPrivateKey.new("Could not find a private key for #{id.inspect}") unless key
|
142
|
+
data = EncryptedDocument.from_data(data).decrypted_data(key, cert)
|
141
143
|
end
|
142
144
|
@serializer.load(data) if data
|
143
145
|
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
|
@@ -37,8 +37,8 @@ module RightScale
|
|
37
37
|
cert = Certificate.load(AgentConfig.certs_file("#{agent_type}.cert"))
|
38
38
|
key = RsaKeyPair.load(AgentConfig.certs_file("#{agent_type}.key"))
|
39
39
|
mapper_cert = Certificate.load(AgentConfig.certs_file("mapper.cert"))
|
40
|
-
store = StaticCertificateStore.new(mapper_cert, mapper_cert)
|
41
|
-
SecureSerializer.init(Serializer.new, agent_id,
|
40
|
+
store = StaticCertificateStore.new(cert, key, mapper_cert, mapper_cert)
|
41
|
+
SecureSerializer.init(Serializer.new, agent_id, store)
|
42
42
|
true
|
43
43
|
end
|
44
44
|
|
@@ -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
|
@@ -90,11 +90,13 @@ module RightScale
|
|
90
90
|
#
|
91
91
|
# === Parameters
|
92
92
|
# packet(String):: Data representing serialized object
|
93
|
+
# id(String|nil):: Optional identifier of source of data for use
|
94
|
+
# in determining who is the receiver
|
93
95
|
#
|
94
96
|
# === Return
|
95
97
|
# (Object):: Unserialized object
|
96
|
-
def load(packet)
|
97
|
-
cascade_serializers(:load, packet, @secure ? [SecureSerializer] : order_serializers(packet))
|
98
|
+
def load(packet, id = nil)
|
99
|
+
cascade_serializers(:load, packet, @secure ? [SecureSerializer] : order_serializers(packet), id)
|
98
100
|
end
|
99
101
|
|
100
102
|
private
|
@@ -112,18 +114,19 @@ module RightScale
|
|
112
114
|
# action(Symbol):: Serialization action: :dump or :load
|
113
115
|
# packet(Object|String):: Object or serialized data on which action is to be performed
|
114
116
|
# serializers(Array):: Serializers to apply in order
|
117
|
+
# id(String):: Optional identifier of source of data for use in determining who is the receiver
|
115
118
|
#
|
116
119
|
# === Return
|
117
120
|
# (String|Object):: Result of serialization action
|
118
121
|
#
|
119
122
|
# === Raises
|
120
123
|
# SerializationError:: If none of the serializers can perform the requested action
|
121
|
-
def cascade_serializers(action, packet, serializers)
|
124
|
+
def cascade_serializers(action, packet, serializers, id = nil)
|
122
125
|
errors = []
|
123
126
|
serializers.map do |serializer|
|
124
127
|
obj = nil
|
125
128
|
begin
|
126
|
-
obj = serializer.
|
129
|
+
obj = serializer == SecureSerializer ? serializer.send(action, packet, id) : serializer.send(action, packet)
|
127
130
|
rescue SecureSerializer::MissingCertificate, SecureSerializer::InvalidSignature => e
|
128
131
|
errors << Log.format("Failed to #{action} with #{serializer.name}", e)
|
129
132
|
rescue Exception => e
|
@@ -142,7 +145,13 @@ module RightScale
|
|
142
145
|
# === Return
|
143
146
|
# (Array):: Ordered serializers
|
144
147
|
def order_serializers(packet)
|
145
|
-
|
148
|
+
# note the following code for getting the ascii value of the first byte is
|
149
|
+
# efficient for a large packet because it returns an enumerator for the
|
150
|
+
# internal byte array. it is actually more efficient than extracting the
|
151
|
+
# first character as a string and converting it to bytes.
|
152
|
+
# also, the following line works for both ruby 1.8 and ruby 1.9 since the
|
153
|
+
# definition of the bracket operator has changed.
|
154
|
+
packet.bytes.first > 127 ? MSGPACK_FIRST_SERIALIZERS : JSON_FIRST_SERIALIZERS
|
146
155
|
end
|
147
156
|
|
148
157
|
end # Serializer
|
data/right_agent.gemspec
CHANGED
@@ -24,8 +24,8 @@ require 'rubygems'
|
|
24
24
|
|
25
25
|
Gem::Specification.new do |spec|
|
26
26
|
spec.name = 'right_agent'
|
27
|
-
spec.version = '0.
|
28
|
-
spec.date = '
|
27
|
+
spec.version = '0.16.2'
|
28
|
+
spec.date = '2013-07-17'
|
29
29
|
spec.authors = ['Lee Kirchhoff', 'Raphael Simon', 'Tony Spataro']
|
30
30
|
spec.email = 'lee@rightscale.com'
|
31
31
|
spec.homepage = 'https://github.com/rightscale/right_agent'
|
@@ -39,10 +39,9 @@ Gem::Specification.new do |spec|
|
|
39
39
|
|
40
40
|
spec.add_dependency('right_support', ['>= 2.4.1', '< 3.0'])
|
41
41
|
spec.add_dependency('right_amqp', '~> 0.4')
|
42
|
-
spec.add_dependency('json', ['
|
42
|
+
spec.add_dependency('json', ['>= 1.4', '<= 1.7.6']) # json_create behavior change in 1.7.7
|
43
43
|
spec.add_dependency('eventmachine', ['>= 0.12.10', '< 2.0'])
|
44
|
-
spec.add_dependency('
|
45
|
-
spec.add_dependency('msgpack', '0.4.4')
|
44
|
+
spec.add_dependency('msgpack', ['>= 0.4.4', '< 0.6'])
|
46
45
|
spec.add_dependency('net-ssh', '~> 2.0')
|
47
46
|
|
48
47
|
if spec.platform.to_s =~ /mswin|mingw/
|
data/spec/agent_spec.rb
CHANGED
@@ -426,9 +426,9 @@ describe RightScale::Agent do
|
|
426
426
|
end
|
427
427
|
end
|
428
428
|
|
429
|
-
it "should publish result from dispatched request to
|
429
|
+
it "should publish result from dispatched request to request reply_to" do
|
430
430
|
run_in_em do
|
431
|
-
request = RightScale::Request.new("/foo/bar", "payload")
|
431
|
+
request = RightScale::Request.new("/foo/bar", "payload", {:reply_to => "response"})
|
432
432
|
@broker.should_receive(:subscribe).with(hsh(:name => @identity), nil, Hash, Proc).
|
433
433
|
and_return(@broker_ids).and_yield(@broker_id, request, @header).once
|
434
434
|
result = flexmock("result")
|
@@ -0,0 +1,330 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2013 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
24
|
+
|
25
|
+
describe RightScale::AgentTagManager do
|
26
|
+
|
27
|
+
include FlexMock::ArgumentTypes
|
28
|
+
|
29
|
+
before(:each) do
|
30
|
+
@log = flexmock(RightScale::Log)
|
31
|
+
@log.should_receive(:error).by_default.and_return { |m| raise RightScale::Log.format(*m) }
|
32
|
+
@identity = "rs-agent-0-0"
|
33
|
+
@agent = flexmock("agent", :identity => @identity)
|
34
|
+
@agent_id1 = "rs-agent-1-1"
|
35
|
+
@agent_id2 = "rs-agent-2-2"
|
36
|
+
@agent_ids = [@agent_id2, @agent_id3]
|
37
|
+
@manager = RightScale::AgentTagManager.instance
|
38
|
+
@manager.agent = @agent
|
39
|
+
@request = flexmock("request", :run => true)
|
40
|
+
@request.should_receive(:callback).and_yield("result").by_default
|
41
|
+
@request.should_receive(:errback).by_default
|
42
|
+
@request.should_receive(:raw_response).and_return("raw response").by_default
|
43
|
+
@idempotent_request = flexmock(RightScale::IdempotentRequest)
|
44
|
+
@tag = "some:tag=value"
|
45
|
+
@tag1 = "other:tag=value"
|
46
|
+
@tags = [@tag, @tag1]
|
47
|
+
@result = nil
|
48
|
+
end
|
49
|
+
|
50
|
+
context :tags do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
54
|
+
{:agent_identity => @identity, :agent_ids => [@identity]},
|
55
|
+
{}).and_return(@request).once.by_default
|
56
|
+
end
|
57
|
+
|
58
|
+
it "retrieves current agent tags" do
|
59
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
|
60
|
+
@manager.tags { |r| @result = r }
|
61
|
+
@result.should == [@tag]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "returns empty array when there is no results hash" do
|
65
|
+
@request.should_receive(:callback).and_yield({}).once
|
66
|
+
@manager.tags { |r| @result = r }
|
67
|
+
@result.should == []
|
68
|
+
end
|
69
|
+
|
70
|
+
it "returns the raw result when result is not a hash" do
|
71
|
+
@request.should_receive(:callback).and_yield("result").once
|
72
|
+
@manager.tags { |r| @result = r }
|
73
|
+
@result.should == "result"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns raw result when :raw option specified" do
|
77
|
+
@request.should_receive(:callback).and_yield("result").once
|
78
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
79
|
+
@manager.tags(:raw => true) { |r| @result = r }
|
80
|
+
@result.should == "raw response"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "forwards timeout option" do
|
84
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
85
|
+
{:agent_identity => @identity, :agent_ids => [@identity]},
|
86
|
+
{:timeout => 9}).and_return(@request).once
|
87
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
|
88
|
+
@manager.tags(:timeout => 9) { |r| @result = r }
|
89
|
+
@result.should == [@tag]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "yields error result and logs error" do
|
93
|
+
@log.should_receive(:error).with(/Failed to query tags/).once
|
94
|
+
@request.should_receive(:errback).and_yield("error").once
|
95
|
+
@request.should_receive(:callback).once
|
96
|
+
@manager.tags { |r| @result = r }
|
97
|
+
@result.should == "error"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context :query_tags do
|
102
|
+
|
103
|
+
it "queries for agents having individual tag" do
|
104
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
105
|
+
{:agent_identity => @identity, :tags => [@tag]},
|
106
|
+
{}).and_return(@request).once
|
107
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_id1 => {"tags" => [@tag]}}).once
|
108
|
+
@manager.query_tags(@tag) { |r| @result = r }
|
109
|
+
@result.should == {@identity => {"tags" => [@tag]}, @agent_id1 => {"tags" => [@tag]}}
|
110
|
+
end
|
111
|
+
|
112
|
+
it "queries for agents having multiple tags" do
|
113
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
114
|
+
{:agent_identity => @identity, :tags => @tags},
|
115
|
+
{}).and_return(@request).once
|
116
|
+
@request.should_receive(:callback).and_yield({@agent_id1 => {"tags" => @tags}}).once
|
117
|
+
@manager.query_tags(@tags) { |r| @result = r }
|
118
|
+
@result.should == {@agent_id1 => {"tags" => @tags}}
|
119
|
+
end
|
120
|
+
|
121
|
+
it "forwards options" do
|
122
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
123
|
+
{:agent_identity => @identity, :tags => @tags},
|
124
|
+
{:timeout => 9}).and_return(@request).once
|
125
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
|
126
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
127
|
+
@manager.query_tags(@tags, :raw => true, :timeout => 9) { |r| @result = r }
|
128
|
+
@result.should == "raw response"
|
129
|
+
end
|
130
|
+
|
131
|
+
it "yields error result and logs error" do
|
132
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
133
|
+
{:agent_identity => @identity, :tags => [@tag]},
|
134
|
+
{}).and_return(@request).once
|
135
|
+
@log.should_receive(:error).with(/Failed to query tags/).once
|
136
|
+
@request.should_receive(:errback).and_yield("error").once
|
137
|
+
@request.should_receive(:callback).once
|
138
|
+
@manager.query_tags(@tag) { |r| @result = r }
|
139
|
+
@result.should == "error"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context :query_tags_raw do
|
144
|
+
|
145
|
+
before(:each) do
|
146
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
147
|
+
end
|
148
|
+
|
149
|
+
it "always yields raw response" do
|
150
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
151
|
+
{:agent_identity => @identity, :tags => [@tag]},
|
152
|
+
{}).and_return(@request).once
|
153
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_id1 => {"tags" => [@tag]}}).once
|
154
|
+
@manager.query_tags_raw(@tag) { |r| @result = r }
|
155
|
+
@result.should == "raw response"
|
156
|
+
end
|
157
|
+
|
158
|
+
it "queries for agents having individual tag and always yields raw response" do
|
159
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
160
|
+
{:agent_identity => @identity, :tags => [@tag]},
|
161
|
+
{}).and_return(@request).once
|
162
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_id1 => {"tags" => [@tag]}}).once
|
163
|
+
@manager.query_tags_raw(@tag) { |r| @result = r }
|
164
|
+
@result.should == "raw response"
|
165
|
+
end
|
166
|
+
|
167
|
+
it "queries for agents having multiple tags always yields raw response" do
|
168
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
169
|
+
{:agent_identity => @identity, :tags => @tags},
|
170
|
+
{}).and_return(@request).once
|
171
|
+
@request.should_receive(:callback).and_yield({@agent_id1 => {"tags" => @tags}}).once
|
172
|
+
@manager.query_tags_raw(@tags) { |r| @result = r }
|
173
|
+
@result.should == "raw response"
|
174
|
+
end
|
175
|
+
|
176
|
+
it "queries for selected agents" do
|
177
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
178
|
+
{:agent_identity => @identity, :agent_ids => @agent_ids, :tags => @tags},
|
179
|
+
{}).and_return(@request).once
|
180
|
+
@request.should_receive(:callback).and_yield({@agent_id1 => {"tags" => @tags}}).once
|
181
|
+
@manager.query_tags_raw(@tags, @agent_ids) { |r| @result = r }
|
182
|
+
@result.should == "raw response"
|
183
|
+
end
|
184
|
+
|
185
|
+
it "forwards timeout option" do
|
186
|
+
@idempotent_request.should_receive(:new).with("/mapper/query_tags",
|
187
|
+
{:agent_identity => @identity, :tags => @tags},
|
188
|
+
{:timeout => 9}).and_return(@request).once
|
189
|
+
@request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
|
190
|
+
@manager.query_tags_raw(@tags, nil, :timeout => 9) { |r| @result = r }
|
191
|
+
@result.should == "raw response"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context :add_tags do
|
196
|
+
|
197
|
+
before(:each) do
|
198
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
199
|
+
@agent.should_receive(:tags).and_return([]).once.by_default
|
200
|
+
@agent.should_receive(:tags=).once.by_default
|
201
|
+
end
|
202
|
+
|
203
|
+
it "adds individual tag to agent" do
|
204
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
205
|
+
{:new_tags => [@tag], :obsolete_tags => []}).and_return(@request).once
|
206
|
+
@manager.add_tags(@tag).should be_true
|
207
|
+
end
|
208
|
+
|
209
|
+
it "adds multiple tags to agent" do
|
210
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
211
|
+
{:new_tags => @tags, :obsolete_tags => []}).and_return(@request).once
|
212
|
+
@manager.add_tags(@tags).should be_true
|
213
|
+
end
|
214
|
+
|
215
|
+
it "optionally yields raw response" do
|
216
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
217
|
+
{:new_tags => @tags, :obsolete_tags => []}).and_return(@request).once
|
218
|
+
@request.should_receive(:callback).and_yield("result").once
|
219
|
+
@manager.add_tags(@tags) { |r| @result = r }
|
220
|
+
@result.should == "raw response"
|
221
|
+
end
|
222
|
+
|
223
|
+
it "updates local tags" do
|
224
|
+
@agent.should_receive(:tags).and_return([@tag1]).once
|
225
|
+
@agent.should_receive(:tags=).should_receive([@tag]).once
|
226
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
227
|
+
{:new_tags => [@tag], :obsolete_tags => []}).and_return(@request).once
|
228
|
+
@manager.add_tags(@tag).should be_true
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context :remove_tags do
|
233
|
+
|
234
|
+
before(:each) do
|
235
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
236
|
+
@agent.should_receive(:tags).and_return([@tag]).once.by_default
|
237
|
+
@agent.should_receive(:tags=).once.by_default
|
238
|
+
end
|
239
|
+
|
240
|
+
it "removes individual tag to agent" do
|
241
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
242
|
+
{:new_tags => [], :obsolete_tags => [@tag]}).and_return(@request).once
|
243
|
+
@manager.remove_tags(@tag).should be_true
|
244
|
+
end
|
245
|
+
|
246
|
+
it "removes multiple tags to agent" do
|
247
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
248
|
+
{:new_tags => [], :obsolete_tags => @tags}).and_return(@request).once
|
249
|
+
@manager.remove_tags(@tags).should be_true
|
250
|
+
end
|
251
|
+
|
252
|
+
it "optionally yields raw response" do
|
253
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
254
|
+
{:new_tags => [], :obsolete_tags => @tags}).and_return(@request).once
|
255
|
+
@request.should_receive(:callback).and_yield("result").once
|
256
|
+
@manager.remove_tags(@tags) { |r| @result = r }
|
257
|
+
@result.should == "raw response"
|
258
|
+
end
|
259
|
+
|
260
|
+
it "updates local tags" do
|
261
|
+
@agent.should_receive(:tags).and_return([]).once
|
262
|
+
@agent.should_receive(:tags=).should_receive([@tag]).once
|
263
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
264
|
+
{:new_tags => [], :obsolete_tags => [@tag]}).and_return(@request).once
|
265
|
+
@manager.remove_tags(@tag).should be_true
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context :update_tags do
|
270
|
+
|
271
|
+
before(:each) do
|
272
|
+
@agent.should_receive(:tags).and_return([]).once.by_default
|
273
|
+
end
|
274
|
+
|
275
|
+
it "checks that agent has been set" do
|
276
|
+
@manager.agent = nil
|
277
|
+
@agent.should_receive(:tags).never
|
278
|
+
lambda { @manager.update_tags([@tag], [@tag1]) }.should raise_error(ArgumentError, "Must set agent= before using tag manager")
|
279
|
+
end
|
280
|
+
|
281
|
+
it "adds and removes tags for agent" do
|
282
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
283
|
+
{:new_tags => [@tag], :obsolete_tags => [@tag1]}).and_return(@request).once
|
284
|
+
@agent.should_receive(:tags=).never
|
285
|
+
@manager.update_tags([@tag], [@tag1]).should be_true
|
286
|
+
end
|
287
|
+
|
288
|
+
it "yields raw response if block given" do
|
289
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
290
|
+
{:new_tags => [@tag], :obsolete_tags => [@tag1]}).and_return(@request).once
|
291
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
292
|
+
@agent.should_receive(:tags=).once
|
293
|
+
@manager.update_tags([@tag], [@tag1]) { |r| @result = r }
|
294
|
+
@result.should == "raw response"
|
295
|
+
end
|
296
|
+
|
297
|
+
it "updates local tags if block given and successful" do
|
298
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
299
|
+
{:new_tags => [@tag], :obsolete_tags => [@tag1]}).and_return(@request).once
|
300
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
301
|
+
@agent.should_receive(:tags=).with([@tag]).once
|
302
|
+
@manager.update_tags([@tag], [@tag1]) { |r| @result = r }
|
303
|
+
@result.should == "raw response"
|
304
|
+
end
|
305
|
+
|
306
|
+
it "yields error result and does not update local tags" do
|
307
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
308
|
+
{:new_tags => [@tag], :obsolete_tags => [@tag1]}).and_return(@request).once
|
309
|
+
@request.should_receive(:raw_response).and_return("error").once
|
310
|
+
@request.should_receive(:errback).and_yield("error").once
|
311
|
+
@request.should_receive(:callback).once
|
312
|
+
@agent.should_receive(:tags=).never
|
313
|
+
@manager.update_tags([@tag], [@tag1]) { |r| @result = r }
|
314
|
+
@result.should == "error"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
context :clear do
|
319
|
+
|
320
|
+
it "clears all agent tags" do
|
321
|
+
@request.should_receive(:raw_response).and_return("raw response").once
|
322
|
+
@agent.should_receive(:tags).and_return(@tags).twice
|
323
|
+
@agent.should_receive(:tags=).with([]).once
|
324
|
+
@idempotent_request.should_receive(:new).with("/mapper/update_tags",
|
325
|
+
{:new_tags => [], :obsolete_tags => @tags}).and_return(@request).once
|
326
|
+
@manager.clear { |r| @result = r }
|
327
|
+
@result.should == "raw response"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|