right_agent 0.5.1

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 (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
@@ -0,0 +1,38 @@
1
+ #-- -*- mode: ruby; encoding: utf-8 -*-
2
+ # Copyright: Copyright (c) 2011 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 NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module RightScale
25
+ module SubprocessFormatting
26
+ module_function
27
+
28
+ SIGNAL_LOOKUP = Signal.list.invert
29
+
30
+ def reason(status)
31
+ if status.exitstatus.nil?
32
+ "terminated with SIG#{SIGNAL_LOOKUP[status.termsig]}"
33
+ else
34
+ "exited with #{status.exitstatus}"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,124 @@
1
+ #
2
+ # Copyright (c) 2009-2011 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
+ # This allows us to define class methods
24
+ class Object
25
+ def metaclass
26
+ class << self
27
+ self
28
+ end
29
+ end
30
+ end
31
+
32
+ module RightScale
33
+
34
+ class Tracer
35
+
36
+ NON_TRACEABLE_CLASSES = [ 'Kernel', 'Module', 'Object' , 'SyslogLogger', 'RightSupport::SystemLogger' ] +
37
+ [ 'RightScale::Tracer', 'RightScale::Multiplexer' ] +
38
+ [ 'RightScale::Log', 'RightScale::Log::Formatter' ]
39
+
40
+ NON_TRACEABLE_METHODS = [ :metaclass, :method_missing, :method_added, :blank_slate_method_added, :[], :[]= ]
41
+
42
+ NON_TRACEABLE_CLASS_METHODS = [ :initialize, :initialize_copy, :inherited, :new, :allocate, :superclass ]
43
+
44
+ # Add logs when entering and exiting instance and class methods
45
+ # defined on given class
46
+ #
47
+ # === Parameters
48
+ # klass(Class):: Class whose methods should be traced
49
+ #
50
+ # === Return
51
+ # true:: Always return true
52
+ def self.add_tracing_to_class(klass)
53
+ return true if NON_TRACEABLE_CLASSES.include?(klass.to_s)
54
+ (klass.public_instance_methods(all=false) + klass.private_instance_methods(all=false) +
55
+ klass.protected_instance_methods(all=false)).each do |m|
56
+ if traceable(m)
57
+ old_m = klass.instance_method(m)
58
+ klass.module_eval <<-EOM
59
+ alias :o_l_d_#{m} :#{m}
60
+ def #{m}(*args, &blk)
61
+ Log.debug("<<< #{klass}##{m}(" + args.map(&:inspect).join(',') + ")")
62
+ res = o_l_d_#{m}(*args, &blk)
63
+ Log.debug(">>> #{klass}##{m}")
64
+ res
65
+ end
66
+ EOM
67
+ end
68
+ end
69
+ (klass.public_methods(all=false) + klass.private_methods(all=false) +
70
+ klass.protected_methods(all=false)).each do |m|
71
+ if traceable(m, static=true)
72
+ old_m = klass.method(m)
73
+ klass.module_eval <<-EOM
74
+ class << self
75
+ alias :o_l_d_#{m} :#{m}
76
+ def #{m}(*args, &blk)
77
+ Log.debug("<<< #{klass}.#{m}(" + args.map(&:inspect).join(',') + ")")
78
+ res = o_l_d_#{m}(*args, &blk)
79
+ Log.debug(">>> #{klass}.#{m}")
80
+ res
81
+ end
82
+ end
83
+ EOM
84
+ end
85
+ end
86
+ true
87
+ end
88
+
89
+ # Can method be traced?
90
+ #
91
+ # === Parameters
92
+ # m(String):: Method name
93
+ # static(Boolean):: Whether method is a class method
94
+ #
95
+ # === Return
96
+ # traceable(Boolean):: true if method can be traced, false otherwise
97
+ def self.traceable(m, static=false)
98
+ traceable = !NON_TRACEABLE_METHODS.include?(m.to_sym) && m =~ /[a-zA-Z0-9]$/
99
+ traceable &&= !NON_TRACEABLE_CLASS_METHODS.include?(m.to_sym) if static
100
+ traceable
101
+ end
102
+
103
+ # Add tracing to all classes in given namespaces
104
+ #
105
+ # === Parameters
106
+ # namespaces(Array|String):: Namespace(s) of classes whose methods should be traced
107
+ #
108
+ # === Return
109
+ # true:: Always return true
110
+ def self.add_tracing_to_namespaces(namespaces)
111
+ namespaces = [ namespaces ] unless namespaces.respond_to?(:inject)
112
+ regexps = namespaces.inject([]) { |reg, n| reg << "^#{n}::" }
113
+ unless regexps.empty?
114
+ ObjectSpace.each_object(Class) do |c|
115
+ if c.to_s =~ /#{regexps.join('|')}/
116
+ add_tracing_to_class(c)
117
+ end
118
+ end
119
+ end
120
+ true
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,60 @@
1
+ # -*-ruby-*-
2
+ # Copyright: Copyright (c) 2011 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 NONINFRINGEMENT.
18
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'rubygems'
24
+
25
+ Gem::Specification.new do |spec|
26
+ spec.name = 'right_agent'
27
+ spec.version = '0.5.1'
28
+ spec.authors = ['Lee Kirchhoff', 'Raphael Simon', 'Tony Spataro']
29
+ spec.email = 'lee@rightscale.com'
30
+ spec.homepage = 'https://github.com/rightscale/right_agent'
31
+ spec.platform = Gem::Platform::RUBY
32
+ spec.summary = 'Agent for interfacing server with RightScale system'
33
+ spec.has_rdoc = true
34
+ spec.rdoc_options = ["--main", "README.rdoc", "--title", "RightAgent"]
35
+ spec.extra_rdoc_files = ["README.rdoc"]
36
+ spec.required_ruby_version = '>= 1.8.7'
37
+ spec.require_path = 'lib'
38
+
39
+ spec.add_dependency('right_support')
40
+ spec.add_dependency('amqp', "0.6.7")
41
+ spec.add_dependency('json', [">= 1.4.4", "<= 1.4.6"])
42
+ spec.add_dependency('eventmachine', "~> 0.12.10")
43
+ spec.add_dependency('right_popen', "~> 1.0.11")
44
+ spec.add_dependency('msgpack', "0.4.4")
45
+
46
+ spec.description = <<-EOF
47
+ RightAgent provides a foundation for running an agent on a server to interface
48
+ in a secure fashion with other agents in the RightScale system. A RightAgent
49
+ uses RabbitMQ as the message bus and the RightScale mapper as the routing node.
50
+ Servers running a RightAgent establish a queue on startup for receiving packets
51
+ routed to it via the mapper. The packets are structured to invoke services in
52
+ the agent represented by actors and methods. The RightAgent may respond to these
53
+ requests with a result packet that the mapper then routes to the originator.
54
+ Similarly a RightAgent can also make requests of other RightAgents in the
55
+ EOF
56
+
57
+ candidates = Dir.glob("{lib,spec}/**/*") +
58
+ ["LICENSE", "README.rdoc", "Rakefile", "right_agent.gemspec"]
59
+ spec.files = candidates.sort
60
+ end
@@ -0,0 +1,81 @@
1
+ #
2
+ # Copyright (c) 2009-2011 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::ActorRegistry do
26
+
27
+ class ::WebDocumentImporter
28
+ include RightScale::Actor
29
+ expose :import, :cancel
30
+
31
+ def import
32
+ 1
33
+ end
34
+ def cancel
35
+ 0
36
+ end
37
+ end
38
+
39
+ module ::Actors
40
+ class ComedyActor
41
+ include RightScale::Actor
42
+ expose :fun_tricks
43
+ def fun_tricks
44
+ :rabbit_in_the_hat
45
+ end
46
+ end
47
+ end
48
+
49
+ before(:each) do
50
+ @registry = RightScale::ActorRegistry.new
51
+ end
52
+
53
+ it "should know about all services" do
54
+ @registry.register(WebDocumentImporter.new, nil)
55
+ @registry.register(Actors::ComedyActor.new, nil)
56
+ @registry.services.sort.should == ["/actors/comedy_actor/fun_tricks", "/web_document_importer/cancel", "/web_document_importer/import"]
57
+ end
58
+
59
+ it "should not register anything except RightScale::Actor" do
60
+ lambda { @registry.register(String.new, nil) }.should raise_error(ArgumentError)
61
+ end
62
+
63
+ it "should register an actor" do
64
+ importer = WebDocumentImporter.new
65
+ @registry.register(importer, nil)
66
+ @registry.actors['web_document_importer'].should == importer
67
+ end
68
+
69
+ it "should log info message that actor was registered" do
70
+ importer = WebDocumentImporter.new
71
+ flexmock(RightScale::Log).should_receive(:info).with("[actor] #{importer.class.to_s}").once
72
+ @registry.register(importer, nil)
73
+ end
74
+
75
+ it "should handle actors registered with a custom prefix" do
76
+ importer = WebDocumentImporter.new
77
+ @registry.register(importer, 'monkey')
78
+ @registry.actor_for('monkey').should == importer
79
+ end
80
+
81
+ end # RightScale::ActorRegistry
@@ -0,0 +1,99 @@
1
+ #
2
+ # Copyright (c) 2009-2011 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::Actor do
26
+ class ::WebDocumentImporter
27
+ include RightScale::Actor
28
+ expose :import, :cancel
29
+
30
+ def import
31
+ 1
32
+ end
33
+ def cancel
34
+ 0
35
+ end
36
+ def continue
37
+ 1
38
+ end
39
+ end
40
+
41
+ module ::Actors
42
+ class ComedyActor
43
+ include RightScale::Actor
44
+ expose :fun_tricks
45
+ def fun_tricks
46
+ :rabbit_in_the_hat
47
+ end
48
+ end
49
+ end
50
+
51
+ class ::Actors::InvalidActor
52
+ include RightScale::Actor
53
+ expose :non_existing
54
+ end
55
+
56
+ describe ".expose" do
57
+ before :each do
58
+ @exposed = WebDocumentImporter.instance_variable_get(:@exposed).dup
59
+ end
60
+
61
+ after :each do
62
+ WebDocumentImporter.instance_variable_set(:@exposed, @exposed)
63
+ end
64
+
65
+
66
+ it "should single expose method only once" do
67
+ 3.times { WebDocumentImporter.expose(:continue) }
68
+ WebDocumentImporter.provides_for("webfiles").should == ["/webfiles/import", "/webfiles/cancel", "/webfiles/continue"]
69
+ end
70
+ end
71
+
72
+ describe ".default_prefix" do
73
+ it "is calculated as default prefix as const path of class name" do
74
+ Actors::ComedyActor.default_prefix.should == "actors/comedy_actor"
75
+ WebDocumentImporter.default_prefix.should == "web_document_importer"
76
+ end
77
+ end
78
+
79
+ describe ".provides_for(prefix)" do
80
+ before :each do
81
+ @provides = Actors::ComedyActor.provides_for("money")
82
+ end
83
+
84
+ it "returns an array" do
85
+ @provides.should be_kind_of(Array)
86
+ end
87
+
88
+ it "maps exposed service methods to prefix" do
89
+ @provides.should == ["/money/fun_tricks"]
90
+ wdi_provides = WebDocumentImporter.provides_for("webfiles")
91
+ wdi_provides.should include("/webfiles/import")
92
+ wdi_provides.should include("/webfiles/cancel")
93
+ end
94
+
95
+ it "should not include methods not existing in the actor class" do
96
+ Actors::InvalidActor.provides_for("money").should_not include("/money/non_existing")
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,226 @@
1
+ #
2
+ # Copyright (c) 2011 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::AgentConfig do
26
+
27
+ before(:all) do
28
+ @agent_config = RightScale::AgentConfig
29
+ @pwd_dir = Dir.pwd
30
+ @tmp_dir = RightScale::Platform.filesystem.temp_dir
31
+ @test_dir = File.join(@tmp_dir, 'agent_config_test')
32
+ @agent_id1 = "rs-agent-1-1"
33
+ FileUtils.mkdir_p(@root_dir1 = File.join(@test_dir, 'root1'))
34
+ FileUtils.mkdir_p(@root_dir2 = File.join(@test_dir, 'root2'))
35
+ FileUtils.mkdir_p(@root_dir3 = File.join(@test_dir, 'root3'))
36
+ FileUtils.mkdir_p(@init_dir1 = File.join(@root_dir1, 'init'))
37
+ FileUtils.mkdir_p(@init_dir2 = File.join(@root_dir2, 'init'))
38
+ FileUtils.mkdir_p(@init_dir3 = File.join(@root_dir3, 'init'))
39
+ FileUtils.touch([@config1 = File.join(@init_dir1, 'config.yml'), @config2 = File.join(@init_dir2, 'config.yml')])
40
+ FileUtils.touch([@init2 = File.join(@init_dir2, 'init.rb'), @init3 = File.join(@init_dir3, 'init.rb')])
41
+ FileUtils.mkdir_p(@actors1 = File.join(@root_dir1, 'actors'))
42
+ FileUtils.mkdir_p(@actors3 = File.join(@root_dir3, 'actors'))
43
+ @actors = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'right_agent', 'actors'))
44
+ FileUtils.mkdir_p(@certs1 = File.join(@root_dir1, 'certs'))
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')])
48
+ FileUtils.touch([@agent_cert2 = File.join(@certs2, 'agent.cert')])
49
+ FileUtils.touch([@mapper_key2 = File.join(@certs2, 'mapper.key')])
50
+ FileUtils.mkdir_p(@lib1 = File.join(@root_dir1, 'lib'))
51
+ FileUtils.mkdir_p(@scripts3 = File.join(@root_dir3, 'scripts'))
52
+ FileUtils.mkdir_p(@cfg_dir = File.join(@test_dir, 'cfg'))
53
+ FileUtils.mkdir_p(@cfg_agent1_dir = File.join(@cfg_dir, 'agent_1'))
54
+ FileUtils.mkdir_p(@pid_dir = File.join(@test_dir, 'pid'))
55
+ @agent_options1 = {
56
+ :identity => @agent_id1,
57
+ :root_dir => [@root_dir1, @root_dir2],
58
+ :log_dir => @tmp_dir,
59
+ :pid_dir => @pid_dir
60
+ }
61
+ File.open(@cfg_agent1 = File.join(@cfg_agent1_dir, 'config.yml'), "w") { |f| f.puts(YAML.dump(@agent_options1)) }
62
+ @agent_id2 = "rs-agent-2-2"
63
+ @agent_options2 = {
64
+ :identity => @agent_id2,
65
+ :root_dir => @root_dir2,
66
+ }
67
+ FileUtils.mkdir_p(@cfg_agent2_dir = File.join(@cfg_dir, 'agent_2'))
68
+ FileUtils.touch([@cfg_agent2 = File.join(@cfg_agent2_dir, 'config.yml')])
69
+ @pid = 12345
70
+ File.open(@cookie_file = File.join(@pid_dir, "#{@agent_id1}.pid"), "w") { |f| f.puts(@pid) }
71
+ @agent_cookie1 = {
72
+ :cookie => "1a2b3c",
73
+ :listen_port => 70000
74
+ }
75
+ File.open(@cookie_file = File.join(@pid_dir, "#{@agent_id1}.cookie"), "w") { |f| f.puts(YAML.dump(@agent_cookie1)) }
76
+ end
77
+
78
+ after(:all) do
79
+ FileUtils.remove_dir(@test_dir)
80
+ end
81
+
82
+ it 'should return protocol version' do
83
+ @agent_config.protocol_version.should == RightScale::AgentConfig::PROTOCOL_VERSION
84
+ end
85
+
86
+ it 'should default root directory to current working directory' do
87
+ @agent_config.root_dir.should == @pwd_dir
88
+ end
89
+
90
+ it 'should set root directory to single directory' do
91
+ @agent_config.root_dir = @root_dir1
92
+ @agent_config.root_dir.should == @root_dir1
93
+ end
94
+
95
+ it 'should set root directory to list of directories' do
96
+ @agent_config.root_dir = [@root_dir1, @root_dir2]
97
+ @agent_config.root_dirs.should == [@root_dir1, @root_dir2]
98
+ end
99
+
100
+ it 'should be able to change root directory' do
101
+ @agent_config.root_dir = [@root_dir1, @root_dir2]
102
+ @agent_config.root_dir.should == [@root_dir1, @root_dir2]
103
+ @agent_config.root_dir = [@root_dir3, @root_dir2]
104
+ @agent_config.root_dir.should == [@root_dir3, @root_dir2]
105
+ @agent_config.root_dir = nil
106
+ @agent_config.root_dir.should == @pwd_dir
107
+ end
108
+
109
+ it 'should default configuration directory to platform specific directory' do
110
+ @agent_config.cfg_dir.should == RightScale::Platform.filesystem.cfg_dir
111
+ end
112
+
113
+ it 'should set configuration directory' do
114
+ @agent_config.cfg_dir = @cfg_dir
115
+ @agent_config.cfg_dir.should == @cfg_dir
116
+ end
117
+
118
+ it 'should default process id directory to platform specific directory' do
119
+ @agent_config.pid_dir.should == RightScale::Platform.filesystem.pid_dir
120
+ end
121
+
122
+ it 'should set process id directory' do
123
+ @agent_config.pid_dir = @pid_dir
124
+ @agent_config.pid_dir.should == @pid_dir
125
+ end
126
+
127
+ it 'should return first init/init.rb file path found' do
128
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
129
+ @agent_config.init_file.should == @init2
130
+ end
131
+
132
+ it 'should return first init/config.yml file path found' do
133
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
134
+ @agent_config.init_cfg_file.should == @config1
135
+ end
136
+
137
+ it 'should return actors directory paths containing all in root directory paths with one from gem last' do
138
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
139
+ @agent_config.actors_dirs.should == [@actors1, @actors3, @actors]
140
+ end
141
+
142
+ it 'should return actors directory paths containing other actors directories' do
143
+ module RightScale
144
+ AgentConfig.module_eval { def self.other_actors_dirs; [RightScale::Platform.filesystem.temp_dir] end }
145
+ end
146
+ @agent_config.root_dir = [@root_dir1, @root_dir2]
147
+ @agent_config.actors_dirs.should == [@actors1, @tmp_dir, @actors]
148
+ end
149
+
150
+ it 'should return first certs file path found' do
151
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
152
+ @agent_config.certs_file('mapper.cert').should == @mapper_cert1
153
+ @agent_config.certs_file('mapper.key').should == @mapper_key2
154
+ @agent_config.certs_file('agent.key').should be_nil
155
+ end
156
+
157
+ it 'should return all certs file paths found without any duplicates and by root directory order' do
158
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
159
+ @agent_config.certs_files('*.cert').should == [@mapper_cert1, @agent_cert2]
160
+ @agent_config.certs_files('*.key').should == [@mapper_key2]
161
+ @agent_config.certs_files('*.abc').should == []
162
+ end
163
+
164
+ it 'should return the first lib directory path' do
165
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
166
+ @agent_config.lib_dir.should == @lib1
167
+ end
168
+
169
+ it 'should return the first scripts directory path' do
170
+ @agent_config.root_dir = [@root_dir1, @root_dir2, @root_dir3]
171
+ @agent_config.scripts_dir.should == @scripts3
172
+ end
173
+
174
+ it 'should return the configuration file path for an agent' do
175
+ @agent_config.cfg_dir = @cfg_dir
176
+ @agent_config.cfg_file('agent_1').should == @cfg_agent1
177
+ @agent_config.cfg_file('no_agent').should == File.join(@cfg_dir, 'no_agent', 'config.yml')
178
+ end
179
+
180
+ it 'should check if agent configuration file exists if requested to' do
181
+ @agent_config.cfg_dir = @cfg_dir
182
+ @agent_config.cfg_file('agent_1', exists = true).should == @cfg_agent1
183
+ @agent_config.cfg_file('no_agent', exists = true).should be_nil
184
+ end
185
+
186
+ it 'should return configuration file paths for all agents' do
187
+ @agent_config.cfg_dir = @cfg_dir
188
+ @agent_config.cfg_files.should =~ [@cfg_agent1, @cfg_agent2]
189
+ end
190
+
191
+ it 'should return a list of all configured agents' do
192
+ @agent_config.cfg_dir = @cfg_dir
193
+ @agent_config.cfg_agents.should =~ ['agent_1', 'agent_2']
194
+ end
195
+
196
+ it 'should return agent name for agent identity' do
197
+ @agent_config.cfg_dir = @cfg_dir
198
+ @agent_config.agent_name(@agent_id1).should == 'agent_1'
199
+ @agent_config.agent_name("rs-bogus-0-0").should be_nil
200
+ end
201
+
202
+ it 'should load agent options from a configuration file and symbolize the keys' do
203
+ @agent_config.cfg_dir = @cfg_dir
204
+ @agent_config.load_cfg('agent_1').should == @agent_options1
205
+ @agent_config.load_cfg("no_agent").should be_nil
206
+ end
207
+
208
+ it 'should store agent options in a configuration file in YAML format' do
209
+ @agent_config.cfg_dir = @cfg_dir
210
+ @agent_config.store_cfg("agent_2", @agent_options2) == @agent_config.cfg_file("agent_2")
211
+ @agent_config.load_cfg('agent_2').should == @agent_options2
212
+ end
213
+
214
+ it 'should return the process id file object for an agent' do
215
+ @agent_config.cfg_dir = @cfg_dir
216
+ @agent_config.pid_file('agent_1').identity.should == @agent_id1
217
+ @agent_config.pid_file('no_agent').should be_nil
218
+ end
219
+
220
+ it 'should return the agent options retrieved from the configuration file and associated pid file' do
221
+ @agent_config.cfg_dir = @cfg_dir
222
+ @agent_config.agent_options('agent_1').should == @agent_options1.merge(@agent_cookie1.merge(:pid => @pid, :log_path => @tmp_dir))
223
+ @agent_config.agent_options("no_agent").should == {}
224
+ end
225
+
226
+ end