right_agent 1.0.1 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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