right_agent 1.0.1 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README.rdoc +10 -8
  2. data/Rakefile +31 -5
  3. data/lib/right_agent.rb +6 -1
  4. data/lib/right_agent/actor.rb +4 -20
  5. data/lib/right_agent/actors/agent_manager.rb +1 -1
  6. data/lib/right_agent/agent.rb +357 -144
  7. data/lib/right_agent/agent_config.rb +7 -6
  8. data/lib/right_agent/agent_identity.rb +13 -11
  9. data/lib/right_agent/agent_tag_manager.rb +60 -64
  10. data/{spec/results_mock.rb → lib/right_agent/clients.rb} +10 -24
  11. data/lib/right_agent/clients/api_client.rb +383 -0
  12. data/lib/right_agent/clients/auth_client.rb +247 -0
  13. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  14. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  15. data/lib/right_agent/clients/right_http_client.rb +279 -0
  16. data/lib/right_agent/clients/router_client.rb +493 -0
  17. data/lib/right_agent/command/command_io.rb +4 -4
  18. data/lib/right_agent/command/command_parser.rb +2 -2
  19. data/lib/right_agent/command/command_runner.rb +1 -1
  20. data/lib/right_agent/connectivity_checker.rb +179 -0
  21. data/lib/right_agent/core_payload_types/secure_document_location.rb +2 -2
  22. data/lib/right_agent/dispatcher.rb +12 -10
  23. data/lib/right_agent/enrollment_result.rb +16 -12
  24. data/lib/right_agent/exceptions.rb +34 -20
  25. data/lib/right_agent/history.rb +10 -5
  26. data/lib/right_agent/log.rb +5 -5
  27. data/lib/right_agent/minimal.rb +1 -0
  28. data/lib/right_agent/multiplexer.rb +1 -1
  29. data/lib/right_agent/offline_handler.rb +270 -0
  30. data/lib/right_agent/packets.rb +7 -7
  31. data/lib/right_agent/payload_formatter.rb +1 -1
  32. data/lib/right_agent/pending_requests.rb +128 -0
  33. data/lib/right_agent/platform.rb +1 -1
  34. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  35. data/lib/right_agent/{idempotent_request.rb → retryable_request.rb} +7 -7
  36. data/lib/right_agent/scripts/agent_controller.rb +28 -26
  37. data/lib/right_agent/scripts/agent_deployer.rb +37 -22
  38. data/lib/right_agent/scripts/common_parser.rb +10 -3
  39. data/lib/right_agent/secure_identity.rb +1 -1
  40. data/lib/right_agent/sender.rb +299 -785
  41. data/lib/right_agent/serialize/secure_serializer.rb +3 -1
  42. data/lib/right_agent/serialize/secure_serializer_initializer.rb +2 -2
  43. data/lib/right_agent/serialize/serializable.rb +8 -3
  44. data/right_agent.gemspec +49 -18
  45. data/spec/agent_config_spec.rb +7 -7
  46. data/spec/agent_identity_spec.rb +7 -4
  47. data/spec/agent_spec.rb +43 -7
  48. data/spec/agent_tag_manager_spec.rb +72 -83
  49. data/spec/clients/api_client_spec.rb +423 -0
  50. data/spec/clients/auth_client_spec.rb +272 -0
  51. data/spec/clients/balanced_http_client_spec.rb +576 -0
  52. data/spec/clients/base_retry_client_spec.rb +635 -0
  53. data/spec/clients/router_client_spec.rb +594 -0
  54. data/spec/clients/spec_helper.rb +111 -0
  55. data/spec/command/command_io_spec.rb +1 -1
  56. data/spec/command/command_parser_spec.rb +1 -1
  57. data/spec/connectivity_checker_spec.rb +83 -0
  58. data/spec/dispatcher_spec.rb +3 -2
  59. data/spec/enrollment_result_spec.rb +2 -2
  60. data/spec/history_spec.rb +51 -39
  61. data/spec/offline_handler_spec.rb +340 -0
  62. data/spec/pending_requests_spec.rb +136 -0
  63. data/spec/{idempotent_request_spec.rb → retryable_request_spec.rb} +73 -73
  64. data/spec/sender_spec.rb +835 -1052
  65. data/spec/serialize/secure_serializer_spec.rb +3 -2
  66. data/spec/spec_helper.rb +54 -1
  67. metadata +71 -12
@@ -26,6 +26,8 @@ module RightScale
26
26
  # X.509 certificate signing
27
27
  class SecureSerializer
28
28
 
29
+ include ProtocolVersionMixin
30
+
29
31
  class MissingPrivateKey < Exception; end
30
32
  class MissingCertificate < Exception; end
31
33
  class InvalidSignature < Exception; end
@@ -90,7 +92,7 @@ module RightScale
90
92
  # Exception:: If certificate identity, certificate store, certificate, or private key missing
91
93
  def dump(obj, encrypt = nil)
92
94
  must_encrypt = encrypt || @encrypt
93
- serialize_format = if obj.respond_to?(:send_version) && obj.send_version >= 12
95
+ serialize_format = if obj.respond_to?(:send_version) && can_handle_msgpack_result?(obj.send_version)
94
96
  @serializer.format
95
97
  else
96
98
  :json
@@ -36,8 +36,8 @@ module RightScale
36
36
  def self.init(agent_type, agent_id)
37
37
  cert = Certificate.load(AgentConfig.certs_file("#{agent_type}.cert"))
38
38
  key = RsaKeyPair.load(AgentConfig.certs_file("#{agent_type}.key"))
39
- mapper_cert = Certificate.load(AgentConfig.certs_file("mapper.cert"))
40
- store = StaticCertificateStore.new(cert, key, mapper_cert, mapper_cert)
39
+ router_cert = Certificate.load(AgentConfig.certs_file("router.cert"))
40
+ store = StaticCertificateStore.new(cert, key, router_cert, router_cert)
41
41
  SecureSerializer.init(Serializer.new, agent_id, store)
42
42
  true
43
43
  end
@@ -128,6 +128,7 @@ module RightScale
128
128
 
129
129
  # Symbolize keys of hash, use when retrieving hashes that use symbols
130
130
  # for keys as JSON and MessagePack serialization will produce strings instead
131
+ # Simply return any object that is not a hash as is
131
132
  #
132
133
  # === Parameters
133
134
  # hash(Hash):: Hash whose keys are to be symbolized
@@ -135,9 +136,13 @@ module RightScale
135
136
  # === Return
136
137
  # (Hash):: Hash with same values but symbol keys
137
138
  def self.symbolize_keys(hash)
138
- hash.inject({}) do |h, (key, value)|
139
- h[(key.to_sym rescue key) || key] = value
140
- h
139
+ if hash.is_a?(Hash)
140
+ hash.inject({}) do |h, (key, value)|
141
+ h[(key.to_sym rescue key) || key] = value
142
+ h
143
+ end
144
+ else
145
+ hash
141
146
  end
142
147
  end
143
148
 
@@ -1,5 +1,5 @@
1
1
  # -*-ruby-*-
2
- # Copyright: Copyright (c) 2011 RightScale, Inc.
2
+ # Copyright: Copyright (c) 2011-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
@@ -21,11 +21,12 @@
21
21
  # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
 
23
23
  require 'rubygems'
24
+ require 'rbconfig'
24
25
 
25
26
  Gem::Specification.new do |spec|
26
27
  spec.name = 'right_agent'
27
- spec.version = '1.0.1'
28
- spec.date = '2013-10-29'
28
+ spec.version = '2.0.7'
29
+ spec.date = '2014-02-28'
29
30
  spec.authors = ['Lee Kirchhoff', 'Raphael Simon', 'Tony Spataro', 'Scott Messier']
30
31
  spec.email = 'lee@rightscale.com'
31
32
  spec.homepage = 'https://github.com/rightscale/right_agent'
@@ -39,29 +40,59 @@ Gem::Specification.new do |spec|
39
40
 
40
41
  spec.add_dependency('right_support', ['>= 2.4.1', '< 3.0'])
41
42
  spec.add_dependency('right_amqp', '~> 0.7')
43
+ spec.add_dependency('rest-client', '1.7.0.alpha')
44
+ spec.add_dependency('faye-websocket', '0.7.0')
42
45
  spec.add_dependency('eventmachine', ['>= 0.12.10', '< 2.0'])
43
46
  spec.add_dependency('net-ssh', '~> 2.0')
44
47
 
45
- # not currently needed by Linux but it does no harm to have it.
46
- spec.add_dependency('ffi')
47
- case RUBY_PLATFORM
48
- when /mswin|mingw/i
49
- spec.add_dependency('win32-dir', '~> 0.4.6')
50
- spec.add_dependency('win32-process', '~> 0.7.3')
51
- when /win32|dos|cygwin/i
48
+ # TEAL HACK: rake gem may override current RUBY_PLATFORM to allow building
49
+ # gems for all supported platforms from any platform. rubygems 1.8.x makes it
50
+ # necessary to produce platform-specific gems with context-sensitive gemspecs
51
+ # in order to retain Windows (or Linux)-specific gem requirements. this works
52
+ # from any platform because there is no native code to pre-compile and package
53
+ # with this gem.
54
+ gem_platform = defined?(::RightScale::MultiPlatformGemTask.gem_platform_override) ?
55
+ ::RightScale::MultiPlatformGemTask.gem_platform_override :
56
+ nil
57
+ gem_platform ||= ::RbConfig::CONFIG['host_os']
58
+ case gem_platform
59
+ when /mswin/i
60
+ spec.add_dependency('win32-api', ['>= 1.4.5', '< 1.4.7'])
61
+ spec.add_dependency('win32-dir', '~> 0.3.5')
62
+ spec.add_dependency('win32-process', '~> 0.6.1')
63
+ spec.add_dependency('msgpack', ['>= 0.4.4', '< 0.5'])
64
+ spec.add_dependency('json', '1.4.6')
65
+ spec.platform = 'x86-mswin32-60'
66
+ when /mingw/i
67
+ spec.add_dependency('ffi')
68
+ spec.add_dependency('win32-dir', '>= 0.3.5')
69
+ spec.add_dependency('win32-process', '>= 0.6.1')
70
+ spec.add_dependency('msgpack', ['>= 0.4.4', '< 0.6'])
71
+ spec.add_dependency('json', '~> 1.4')
72
+ spec.platform = 'x86-mingw32'
73
+ when /win32|dos|cygwin|windows/i
52
74
  raise ::NotImplementedError, 'Unsupported Ruby-on-Windows variant'
75
+ else
76
+ # ffi is not currently needed by Linux but it does no harm to have it and it
77
+ # allows bundler to generate a consistent Gemfile.lock when it is declared
78
+ # for both mingw and Linux.
79
+ spec.add_dependency('ffi')
80
+ spec.add_dependency('msgpack', ['>= 0.4.4', '< 0.6'])
81
+ spec.add_dependency('json', '~> 1.4')
53
82
  end
54
- spec.add_dependency('msgpack', ['>= 0.4.4', '< 0.6'])
55
- spec.add_dependency('json', '~> 1.4')
56
83
 
57
84
  spec.description = <<-EOF
58
85
  RightAgent provides a foundation for running an agent on a server to interface
59
- in a secure fashion with other agents in the RightScale system. A RightAgent
60
- uses RabbitMQ as the message bus and the RightScale mapper as the routing node.
61
- Servers running a RightAgent establish a queue on startup for receiving packets
62
- routed to it via the mapper. The packets are structured to invoke services in
63
- the agent represented by actors and methods. The RightAgent may respond to these
64
- requests with a result packet that the mapper then routes to the originator.
86
+ in a secure fashion with other agents in the RightScale system using RightNet,
87
+ which operates in either HTTP or AMQP mode. When using HTTP, RightAgent
88
+ makes requests to RightApi servers and receives requests using long-polling or
89
+ WebSockets via the RightNet router. To respond to requests it posts to the
90
+ HTTP router. When using AMQP, RightAgent uses RabbitMQ as the message bus and
91
+ the RightNet router as the routing node to make requests; to receives requests
92
+ routed to it by the RightNet router, it establishes a queue on startup. The
93
+ packets are structured to invoke services in the agent represented by actors
94
+ and methods. The RightAgent may respond to these requests with a result packet
95
+ that the router then routes to the originator.
65
96
  EOF
66
97
 
67
98
  candidates = Dir.glob("{lib,spec}/**/*") +
@@ -43,10 +43,10 @@ describe RightScale::AgentConfig do
43
43
  @actors = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'right_agent', 'actors'))
44
44
  FileUtils.mkdir_p(@certs1 = File.join(@root_dir1, 'certs'))
45
45
  FileUtils.mkdir_p(@certs2 = File.join(@root_dir2, 'certs'))
46
- FileUtils.touch([@mapper_cert1 = File.join(@certs1, 'mapper.cert')])
47
- FileUtils.touch([@mapper_cert2 = File.join(@certs2, 'mapper.cert')])
46
+ FileUtils.touch([@router_cert1 = File.join(@certs1, 'router.cert')])
47
+ FileUtils.touch([@router_cert2 = File.join(@certs2, 'router.cert')])
48
48
  FileUtils.touch([@agent_cert2 = File.join(@certs2, 'agent.cert')])
49
- FileUtils.touch([@mapper_key2 = File.join(@certs2, 'mapper.key')])
49
+ FileUtils.touch([@router_key2 = File.join(@certs2, 'router.key')])
50
50
  FileUtils.mkdir_p(@lib1 = File.join(@root_dir1, 'lib'))
51
51
  FileUtils.mkdir_p(@scripts3 = File.join(@root_dir3, 'scripts'))
52
52
  FileUtils.mkdir_p(@cfg_dir = File.join(@test_dir, 'cfg'))
@@ -150,15 +150,15 @@ describe RightScale::AgentConfig do
150
150
 
151
151
  it 'should return first certs file path found' do
152
152
  @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
153
- @agent_config.certs_file('mapper.cert').should == @mapper_cert1
154
- @agent_config.certs_file('mapper.key').should == @mapper_key2
153
+ @agent_config.certs_file('router.cert').should == @router_cert1
154
+ @agent_config.certs_file('router.key').should == @router_key2
155
155
  @agent_config.certs_file('agent.key').should be_nil
156
156
  end
157
157
 
158
158
  it 'should return all certs file paths found without any duplicates and by root directory order' do
159
159
  @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
160
- @agent_config.certs_files('*.cert').should == [@mapper_cert1, @agent_cert2]
161
- @agent_config.certs_files('*.key').should == [@mapper_key2]
160
+ @agent_config.certs_files('*.cert').should == [@router_cert1, @agent_cert2]
161
+ @agent_config.certs_files('*.key').should == [@router_key2]
162
162
  @agent_config.certs_files('*.abc').should == []
163
163
  end
164
164
 
@@ -56,20 +56,23 @@ describe RightScale::AgentIdentity do
56
56
  RightScale::AgentIdentity.valid?(id.to_s).should be_true
57
57
  end
58
58
 
59
- it "should treat serialized id with nanite or mapper prefix as valid" do
59
+ it "should treat serialized id with nanite, mapper, or router prefix as valid" do
60
60
  RightScale::AgentIdentity.valid?("nanite-prefix-agent_type-token-1").should be_true
61
61
  RightScale::AgentIdentity.valid?("mapper-prefix-agent_type-token-1").should be_true
62
+ RightScale::AgentIdentity.valid?("router-prefix-agent_type-token-1").should be_true
62
63
  end
63
64
 
64
- it "should parse serialized id with nanite or mapper prefix but discard this prefix" do
65
+ it "should parse serialized id with nanite, mapper, or router prefix but discard this prefix" do
65
66
  RightScale::AgentIdentity.parse("nanite-prefix-agent_type-token-1").to_s.should == "prefix-agent_type-token-1"
66
67
  RightScale::AgentIdentity.parse("mapper-prefix-agent_type-token-1").to_s.should == "prefix-agent_type-token-1"
68
+ RightScale::AgentIdentity.parse("router-prefix-agent_type-token-1").to_s.should == "prefix-agent_type-token-1"
67
69
  end
68
70
 
69
71
  it 'should prefix with nanite to make backward compatible' do
70
72
  id = RightScale::AgentIdentity.new('prefix', 'agent_type', 1, 'token')
71
- RightScale::AgentIdentity.compatible_serialized(id.to_s, 10).should == "prefix-agent_type-token-1"
72
- RightScale::AgentIdentity.compatible_serialized(id.to_s, 9).should == "nanite-prefix-agent_type-token-1"
73
+ RightScale::AgentIdentity.compatible_serialized(id.to_s, version_can_handle_non_nanite_ids).should == "prefix-agent_type-token-1"
74
+ RightScale::AgentIdentity.compatible_serialized(id.to_s, version_cannot_handle_non_nanite_ids).should == "nanite-prefix-agent_type-token-1"
75
+ RightScale::AgentIdentity.compatible_serialized(id.to_s).should == "prefix-agent_type-token-1"
73
76
  end
74
77
 
75
78
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2009-2012 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
@@ -309,7 +309,7 @@ describe RightScale::Agent do
309
309
  @agent.instance_variable_get(:@remaining_queue_setup).should == {@identity => @broker_ids.last(1)}
310
310
  @sender.should_receive(:send_push).with("/registrar/connect", {:agent_identity => @identity, :host => "123",
311
311
  :port => 2, :id => 1, :priority => 1}).once
312
- @agent.__send__(:check_status)
312
+ @agent.send(:check_status)
313
313
  end
314
314
  end
315
315
 
@@ -539,6 +539,38 @@ describe RightScale::Agent do
539
539
  end
540
540
  end
541
541
 
542
+ it "should sleep before an abnormal termination that is following a crash" do
543
+ run_in_em do
544
+ @agent = RightScale::Agent.new(:user => "me", :identity => @identity)
545
+ @broker.should_receive(:nil?).and_return(true)
546
+ @log.should_receive(:error).with(/Terminating because just because/, Exception, :trace).once
547
+ @log.should_receive(:info).with(/Delaying termination for 10 sec/).once.ordered
548
+ @log.should_receive(:info).with(/Terminating immediately/).once.ordered
549
+ now = Time.now
550
+ flexmock(Time).should_receive(:now).and_return(now)
551
+ flexmock(@agent.instance_variable_get(:@history)).should_receive(:analyze_service).
552
+ and_return({:last_crashed => true, :last_crash_time => now.to_i - 5}).once
553
+ flexmock(@agent).should_receive(:sleep).with(10).once
554
+ @agent.terminate("just because", Exception.new("error"))
555
+ end
556
+ end
557
+
558
+ it "should limit the sleep time before an abnormal termination" do
559
+ run_in_em do
560
+ @agent = RightScale::Agent.new(:user => "me", :identity => @identity)
561
+ @broker.should_receive(:nil?).and_return(true)
562
+ @log.should_receive(:error).with(/Terminating because just because/, Exception, :trace).once
563
+ @log.should_receive(:info).with(/Delaying termination for 60 min 0 sec/).once.ordered
564
+ @log.should_receive(:info).with(/Terminating immediately/).once.ordered
565
+ now = Time.now
566
+ flexmock(Time).should_receive(:now).and_return(now)
567
+ flexmock(@agent.instance_variable_get(:@history)).should_receive(:analyze_service).
568
+ and_return({:last_crashed => true, :last_crash_time => now.to_i - 1801}).once
569
+ flexmock(@agent).should_receive(:sleep).with(3600).once
570
+ @agent.terminate("just because", Exception.new("error"))
571
+ end
572
+ end
573
+
542
574
  it "should close unusable broker connections at start of termination" do
543
575
  @broker.should_receive(:unusable).and_return(["rs-broker-123-1"]).once
544
576
  @broker.should_receive(:close_one).with("rs-broker-123-1", false).once
@@ -611,11 +643,13 @@ describe RightScale::Agent do
611
643
  @broker.should_receive(:close).and_yield.once
612
644
  flexmock(EM::Timer).should_receive(:new).with(20, Proc).and_return(@timer).and_yield.once
613
645
  run_in_em do
646
+ called = 0
647
+ callback = lambda { called += 1}
614
648
  @agent = RightScale::Agent.new(:user => "me", :identity => @identity)
649
+ @agent.instance_variable_set(:@terminate_callback, callback)
615
650
  flexmock(@agent).should_receive(:load_actors).and_return(true)
616
651
  @agent.run
617
- called = 0
618
- @agent.terminate { called += 1 }
652
+ @agent.terminate
619
653
  called.should == 1
620
654
  end
621
655
  end
@@ -647,13 +681,15 @@ describe RightScale::Agent do
647
681
  @timer.should_receive(:cancel).once
648
682
  @periodic_timer.should_receive(:cancel).once
649
683
  run_in_em do
684
+ called = 0
685
+ callback = lambda { called += 1}
650
686
  @agent = RightScale::Agent.new(:user => "me", :identity => @identity)
687
+ @agent.instance_variable_set(:@terminate_callback, callback)
651
688
  flexmock(@agent).should_receive(:load_actors).and_return(true)
652
689
  @agent.run
653
- called = 0
654
- @agent.terminate { called += 1 }
690
+ @agent.terminate
655
691
  called.should == 0
656
- @agent.terminate { called += 1 }
692
+ @agent.terminate
657
693
  called.should == 1
658
694
  end
659
695
  end
@@ -29,18 +29,18 @@ describe RightScale::AgentTagManager do
29
29
  before(:each) do
30
30
  @log = flexmock(RightScale::Log)
31
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]
32
+ @identity = "rs-agent-1-1"
33
+ @agent_href = "/api/clouds/1/instances/1"
34
+ @agent_href2 = "/api/clouds/2/instances/2"
35
+ @agent = flexmock("agent", :self_href => @agent_href, :identity => @identity)
36
+ @hrefs = [@agent_href, @agent_href2]
37
37
  @manager = RightScale::AgentTagManager.instance
38
38
  @manager.agent = @agent
39
39
  @request = flexmock("request", :run => true)
40
40
  @request.should_receive(:callback).and_yield("result").by_default
41
41
  @request.should_receive(:errback).by_default
42
42
  @request.should_receive(:raw_response).and_return("raw response").by_default
43
- @idempotent_request = flexmock(RightScale::IdempotentRequest)
43
+ @retryable_request = flexmock(RightScale::RetryableRequest)
44
44
  @tag = "some:tag=value"
45
45
  @tag1 = "other:tag=value"
46
46
  @tags = [@tag, @tag1]
@@ -50,13 +50,12 @@ describe RightScale::AgentTagManager do
50
50
  context :tags do
51
51
 
52
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
53
+ @retryable_request.should_receive(:new).with("/router/query_tags",
54
+ {:agent_identity => @identity, :hrefs => [@agent_href]}, {}).and_return(@request).once.by_default
56
55
  end
57
56
 
58
57
  it "retrieves current agent tags" do
59
- @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
58
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => [@tag]}}).once
60
59
  @manager.tags { |r| @result = r }
61
60
  @result.should == [@tag]
62
61
  end
@@ -81,10 +80,9 @@ describe RightScale::AgentTagManager do
81
80
  end
82
81
 
83
82
  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
83
+ @retryable_request.should_receive(:new).with("/router/query_tags",
84
+ {:agent_identity => @identity, :hrefs => [@agent_href]}, {:timeout => 9}).and_return(@request).once
85
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => [@tag]}}).once
88
86
  @manager.tags(:timeout => 9) { |r| @result = r }
89
87
  @result.should == [@tag]
90
88
  end
@@ -101,27 +99,24 @@ describe RightScale::AgentTagManager do
101
99
  context :query_tags do
102
100
 
103
101
  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
102
+ @retryable_request.should_receive(:new).with("/router/query_tags",
103
+ {:agent_identity => @identity, :tags => [@tag]}, {}).and_return(@request).once
104
+ @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_href => {"tags" => [@tag]}}).once
108
105
  @manager.query_tags(@tag) { |r| @result = r }
109
- @result.should == {@identity => {"tags" => [@tag]}, @agent_id1 => {"tags" => [@tag]}}
106
+ @result.should == {@identity => {"tags" => [@tag]}, @agent_href => {"tags" => [@tag]}}
110
107
  end
111
108
 
112
109
  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
110
+ @retryable_request.should_receive(:new).with("/router/query_tags",
111
+ {:agent_identity => @identity, :tags => @tags}, {}).and_return(@request).once
112
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => @tags}}).once
117
113
  @manager.query_tags(@tags) { |r| @result = r }
118
- @result.should == {@agent_id1 => {"tags" => @tags}}
114
+ @result.should == {@agent_href => {"tags" => @tags}}
119
115
  end
120
116
 
121
117
  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
118
+ @retryable_request.should_receive(:new).with("/router/query_tags",
119
+ {:agent_identity => @identity, :tags => @tags}, {:timeout => 9}).and_return(@request).once
125
120
  @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
126
121
  @request.should_receive(:raw_response).and_return("raw response").once
127
122
  @manager.query_tags(@tags, :raw => true, :timeout => 9) { |r| @result = r }
@@ -129,9 +124,8 @@ describe RightScale::AgentTagManager do
129
124
  end
130
125
 
131
126
  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
127
+ @retryable_request.should_receive(:new).with("/router/query_tags",
128
+ {:agent_identity => @identity, :tags => [@tag]}, {}).and_return(@request).once
135
129
  @log.should_receive(:error).with(/Failed to query tags/).once
136
130
  @request.should_receive(:errback).and_yield("error").once
137
131
  @request.should_receive(:callback).once
@@ -147,45 +141,40 @@ describe RightScale::AgentTagManager do
147
141
  end
148
142
 
149
143
  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
144
+ @retryable_request.should_receive(:new).with("/router/query_tags",
145
+ {:agent_identity => @identity, :tags => [@tag]}, {}).and_return(@request).once
146
+ @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_href => {"tags" => [@tag]}}).once
154
147
  @manager.query_tags_raw(@tag) { |r| @result = r }
155
148
  @result.should == "raw response"
156
149
  end
157
150
 
158
151
  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
152
+ @retryable_request.should_receive(:new).with("/router/query_tags",
153
+ {:agent_identity => @identity, :tags => [@tag]}, {}).and_return(@request).once
154
+ @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}, @agent_href => {"tags" => [@tag]}}).once
163
155
  @manager.query_tags_raw(@tag) { |r| @result = r }
164
156
  @result.should == "raw response"
165
157
  end
166
158
 
167
159
  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
160
+ @retryable_request.should_receive(:new).with("/router/query_tags",
161
+ {:agent_identity => @identity, :tags => @tags}, {}).and_return(@request).once
162
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => @tags}}).once
172
163
  @manager.query_tags_raw(@tags) { |r| @result = r }
173
164
  @result.should == "raw response"
174
165
  end
175
166
 
176
167
  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 }
168
+ @retryable_request.should_receive(:new).with("/router/query_tags",
169
+ {:agent_identity => @identity, :hrefs => @hrefs, :tags => @tags}, {}).and_return(@request).once
170
+ @request.should_receive(:callback).and_yield({@agent_href => {"tags" => @tags}}).once
171
+ @manager.query_tags_raw(@tags, @hrefs) { |r| @result = r }
182
172
  @result.should == "raw response"
183
173
  end
184
174
 
185
175
  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
176
+ @retryable_request.should_receive(:new).with("/router/query_tags",
177
+ {:agent_identity => @identity, :tags => @tags}, {:timeout => 9}).and_return(@request).once
189
178
  @request.should_receive(:callback).and_yield({@identity => {"tags" => [@tag]}}).once
190
179
  @manager.query_tags_raw(@tags, nil, :timeout => 9) { |r| @result = r }
191
180
  @result.should == "raw response"
@@ -201,20 +190,17 @@ describe RightScale::AgentTagManager do
201
190
  end
202
191
 
203
192
  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
193
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
206
194
  @manager.add_tags(@tag).should be_true
207
195
  end
208
196
 
209
197
  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
198
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}).and_return(@request).once
212
199
  @manager.add_tags(@tags).should be_true
213
200
  end
214
201
 
215
202
  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
203
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => @tags}).and_return(@request).once
218
204
  @request.should_receive(:callback).and_yield("result").once
219
205
  @manager.add_tags(@tags) { |r| @result = r }
220
206
  @result.should == "raw response"
@@ -223,8 +209,7 @@ describe RightScale::AgentTagManager do
223
209
  it "updates local tags" do
224
210
  @agent.should_receive(:tags).and_return([@tag1]).once
225
211
  @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
212
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
228
213
  @manager.add_tags(@tag).should be_true
229
214
  end
230
215
  end
@@ -238,20 +223,17 @@ describe RightScale::AgentTagManager do
238
223
  end
239
224
 
240
225
  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
226
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}).and_return(@request).once
243
227
  @manager.remove_tags(@tag).should be_true
244
228
  end
245
229
 
246
230
  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
231
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
249
232
  @manager.remove_tags(@tags).should be_true
250
233
  end
251
234
 
252
235
  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
236
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
255
237
  @request.should_receive(:callback).and_yield("result").once
256
238
  @manager.remove_tags(@tags) { |r| @result = r }
257
239
  @result.should == "raw response"
@@ -260,13 +242,12 @@ describe RightScale::AgentTagManager do
260
242
  it "updates local tags" do
261
243
  @agent.should_receive(:tags).and_return([]).once
262
244
  @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
245
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag]}).and_return(@request).once
265
246
  @manager.remove_tags(@tag).should be_true
266
247
  end
267
248
  end
268
249
 
269
- context :update_tags do
250
+ context :do_update do
270
251
 
271
252
  before(:each) do
272
253
  @agent.should_receive(:tags).and_return([]).once.by_default
@@ -275,42 +256,51 @@ describe RightScale::AgentTagManager do
275
256
  it "checks that agent has been set" do
276
257
  @manager.agent = nil
277
258
  @agent.should_receive(:tags).never
278
- lambda { @manager.update_tags([@tag], [@tag1]) }.should raise_error(ArgumentError, "Must set agent= before using tag manager")
259
+ lambda { @manager.send(:do_update, [@tag], []) }.should \
260
+ raise_error(ArgumentError, "Must set agent= before using tag manager")
261
+ end
262
+
263
+ it "does not allow both add and removal of tags in same request" do
264
+ @agent.should_receive(:tags).never
265
+ lambda { @manager.send(:do_update, [@tag], [@tag1]) }.should \
266
+ raise_error(ArgumentError, "Cannot add and remove tags in same update")
267
+ end
268
+
269
+ it "adds tags for agent" do
270
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
271
+ @agent.should_receive(:tags=).never
272
+ @manager.send(:do_update, [@tag], []).should be_true
279
273
  end
280
274
 
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
275
+ it "removes tags for agent" do
276
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => [@tag1]}).and_return(@request).once
284
277
  @agent.should_receive(:tags=).never
285
- @manager.update_tags([@tag], [@tag1]).should be_true
278
+ @manager.send(:do_update, [], [@tag1]).should be_true
286
279
  end
287
280
 
288
281
  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
282
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
291
283
  @request.should_receive(:raw_response).and_return("raw response").once
292
284
  @agent.should_receive(:tags=).once
293
- @manager.update_tags([@tag], [@tag1]) { |r| @result = r }
285
+ @manager.send(:do_update, [@tag], []) { |r| @result = r }
294
286
  @result.should == "raw response"
295
287
  end
296
288
 
297
289
  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
290
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
300
291
  @request.should_receive(:raw_response).and_return("raw response").once
301
292
  @agent.should_receive(:tags=).with([@tag]).once
302
- @manager.update_tags([@tag], [@tag1]) { |r| @result = r }
293
+ @manager.send(:do_update, [@tag], []) { |r| @result = r }
303
294
  @result.should == "raw response"
304
295
  end
305
296
 
306
297
  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
298
+ @retryable_request.should_receive(:new).with("/router/add_tags", {:tags => [@tag]}).and_return(@request).once
309
299
  @request.should_receive(:raw_response).and_return("error").once
310
300
  @request.should_receive(:errback).and_yield("error").once
311
301
  @request.should_receive(:callback).once
312
302
  @agent.should_receive(:tags=).never
313
- @manager.update_tags([@tag], [@tag1]) { |r| @result = r }
303
+ @manager.send(:do_update, [@tag], []) { |r| @result = r }
314
304
  @result.should == "error"
315
305
  end
316
306
  end
@@ -321,8 +311,7 @@ describe RightScale::AgentTagManager do
321
311
  @request.should_receive(:raw_response).and_return("raw response").once
322
312
  @agent.should_receive(:tags).and_return(@tags).twice
323
313
  @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
314
+ @retryable_request.should_receive(:new).with("/router/delete_tags", {:tags => @tags}).and_return(@request).once
326
315
  @manager.clear { |r| @result = r }
327
316
  @result.should == "raw response"
328
317
  end