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,403 @@
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
+ module RightScale
24
+
25
+ # Helper methods for accessing RightAgent files, directories, and processes.
26
+ # Values returned are driven by root_dir, cfg_dir, and pid_dir, which may be set
27
+ # but have defaults, and secondarily by the contents of the associated agent
28
+ # configuration file generated by the 'rad' tool.
29
+ #
30
+ # The root_dir may be specified to be a list of root directories to be searched
31
+ # when looking for agent files. It defaults to the current working directory.
32
+ # A root directory is assumed to contain some or all of the following directories:
33
+ # init - initialization code
34
+ # actors - actor code
35
+ # certs - security certificates and keys
36
+ # lib - additional agent code
37
+ # scripts - tools code
38
+ #
39
+ # The init directory contains the following initialization code:
40
+ # config.yml - static configuration settings for the agent
41
+ # init.rb - code that registers the agent's actors and performs any other
42
+ # agent specific initialization such as initializing its
43
+ # secure serializer and its command protocol server
44
+ #
45
+ # The certs directory contains the x.509 public certificate and keys needed
46
+ # to sign and encrypt all outgoing messages as well as to check the signature
47
+ # and decrypt any incoming messages. This directory should contain at least:
48
+ # <agent name>.key - agent's' private key
49
+ # <agent name>.cert - agent's' public certificate
50
+ # mapper.cert - mapper's' public certificate
51
+ #
52
+ # The scripts directory at a minimum contains the following:
53
+ # install.sh - script for installing standard and agent specific tools in /usr/bin
54
+ #
55
+ # The cfg_dir is the path to the directory containing a directory for each agent
56
+ # configured on the local machine (e.g., core, core_2, core_3). Each agent directory
57
+ # in turn contains a config.yml file generated to contain that agent's current
58
+ # configuration. The cfg_dir defaults to the platform specific cfg_dir.
59
+ #
60
+ # The pid_dir is the path to the directory where agent process id files are stored.
61
+ # These files are typically named <agent identity>.pid. The pid_dir defaults to the
62
+ # current to the platform specific pid_dir.
63
+ module AgentConfig
64
+
65
+ # Current agent protocol version
66
+ PROTOCOL_VERSION = 17
67
+
68
+ # Current agent protocol version
69
+ #
70
+ # === Return
71
+ # (Integer):: Protocol version
72
+ def self.protocol_version
73
+ PROTOCOL_VERSION
74
+ end
75
+
76
+ # Initialize path to root directory of agent
77
+ #
78
+ # === Parameters
79
+ # dir(String|Array):: Directory path or ordered list of directory paths to be searched
80
+ #
81
+ # === Return
82
+ # (String):: Ordered list of directory paths to be searched
83
+ def self.root_dir=(dir)
84
+ @root_dirs = array(dir)
85
+ end
86
+
87
+ # Initialize path to directory containing generated agent configuration files
88
+ #
89
+ # === Parameters
90
+ # dir(String):: Directory path
91
+ #
92
+ # === Return
93
+ # (String):: Directory path
94
+ def self.cfg_dir=(dir)
95
+ @cfg_dir = dir
96
+ end
97
+
98
+ # Initialize path to directory containing agent process id files
99
+ #
100
+ # === Parameters
101
+ # dir(String):: Directory path
102
+ #
103
+ # === Return
104
+ # (String):: Directory path
105
+ def self.pid_dir=(dir)
106
+ @pid_dir = dir
107
+ end
108
+
109
+ # Root directory path(s)
110
+ #
111
+ # === Return
112
+ # (String|Array):: Individual directory path if only one, otherwise array of paths
113
+ def self.root_dir
114
+ (d = root_dirs).size > 1 ? d : d.first
115
+ end
116
+
117
+ # Path to agent init.rb file containing code that registers the agent's actors
118
+ # and performs any other agent specific initialization such as initializing its
119
+ # secure serializer and its command protocol server
120
+ #
121
+ # === Return
122
+ # (String|nil):: File path name, or nil if file does not exist
123
+ def self.init_file
124
+ first_file(:init_dir, "init.rb")
125
+ end
126
+
127
+ # Path to agent config.yml file containing static configuration settings
128
+ #
129
+ # === Return
130
+ # (String|nil):: File path name, or nil if file does not exist
131
+ def self.init_cfg_file
132
+ first_file(:init_dir, "config.yml")
133
+ end
134
+
135
+ # Ordered list of directory path names for searching for actors:
136
+ # - actors directory in each configured root directory
137
+ # - other directories produced by other_actors_dirs method, e.g., in other associated gems
138
+ # - actors directory in RightAgent gem
139
+ #
140
+ # === Return
141
+ # actors_dirs(Array):: List of directory path names
142
+ def self.actors_dirs
143
+ actors_dirs = all_dirs(:actors_dir)
144
+ actors_dirs += other_actors_dirs if self.respond_to?(:other_actors_dirs)
145
+ actors_dirs << File.normalize_path(File.join(File.dirname(__FILE__), 'actors'))
146
+ actors_dirs
147
+ end
148
+
149
+ # Path to directory containing certificates
150
+ #
151
+ # === Parameters
152
+ # root_dir(String|nil):: Specific root dir to use (must be in root_dirs),
153
+ # if nil use first dir in root_dirs
154
+ #
155
+ # === Return
156
+ # (String|nil):: Path to certs directory, or nil if cannot determine root_dir
157
+ def self.certs_dir(root_dir = nil)
158
+ if root_dir
159
+ root_dir = nil unless @root_dirs && @root_dirs.include?(root_dir)
160
+ else
161
+ root_dir = @root_dirs.first if @root_dirs
162
+ end
163
+ File.normalize_path(File.join(root_dir, "certs")) if root_dir
164
+ end
165
+
166
+ # Path to security file containing X.509 data
167
+ #
168
+ # === Parameters
169
+ # name(String):: Security file name
170
+ #
171
+ # === Return
172
+ # file(String|nil):: File path name, or nil if file does not exist
173
+ def self.certs_file(name)
174
+ first_file(:certs_dir, name)
175
+ end
176
+
177
+ # All security files matching pattern
178
+ #
179
+ # === Parameters
180
+ # pattern(String):: Pattern for security files of interest, e.g., '*.cert'
181
+ #
182
+ # === Return
183
+ # files(Array):: Path name of files found
184
+ def self.certs_files(pattern)
185
+ files = []
186
+ names = []
187
+ all_dirs(:certs_dir).each do |d|
188
+ certs = Dir.glob(File.join(d, pattern)).each do |f|
189
+ unless names.include?(b = File.basename(f))
190
+ files << f
191
+ names << b
192
+ end
193
+ end
194
+ end
195
+ files
196
+ end
197
+
198
+ # Path to first agent lib directory
199
+ #
200
+ # === Return
201
+ # dir(String):: Directory path name
202
+ def self.lib_dir
203
+ all_dirs(:lib_dir2).first
204
+ end
205
+
206
+ # Path to first agent scripts directory
207
+ #
208
+ # === Return
209
+ # dir(String):: Directory path name
210
+ def self.scripts_dir
211
+ all_dirs(:scripts_dir2).first
212
+ end
213
+
214
+ # Path to directory containing a directory for each agent configured on the local machine
215
+ #
216
+ # === Return
217
+ # (String):: Directory path name
218
+ def self.cfg_dir
219
+ @cfg_dir ||= Platform.filesystem.cfg_dir
220
+ end
221
+
222
+ # Path to generated agent configuration file
223
+ #
224
+ # === Parameters
225
+ # agent_name(String):: Agent name
226
+ # exists(Boolean):: Whether to return nil if does not exist
227
+ #
228
+ # === Return
229
+ # (String):: Configuration file path name, or nil if file does not exist
230
+ def self.cfg_file(agent_name, exists = false)
231
+ file = File.normalize_path(File.join(cfg_dir, agent_name, "config.yml"))
232
+ file = nil unless !exists || File.exist?(file)
233
+ file
234
+ end
235
+
236
+ # Configuration file path names for all agents configured locally
237
+ #
238
+ # === Return
239
+ # (Array):: Agent configuration file path names
240
+ def self.cfg_files
241
+ Dir.glob(File.join(cfg_dir, "**", "*.yml"))
242
+ end
243
+
244
+ # Configured agents i.e. agents that have a configuration file
245
+ #
246
+ # === Return
247
+ # (Array):: Name of configured agents
248
+ def self.cfg_agents
249
+ cfg_files.map { |c| File.basename(File.dirname(c)) }
250
+ end
251
+
252
+ # Agent name associated with given agent identity
253
+ #
254
+ # === Parameters
255
+ # agent_id(String):: Serialized agent identity
256
+ #
257
+ # === Return
258
+ # (String|nil):: Agent name, or nil if agent not found
259
+ def self.agent_name(agent_id)
260
+ cfg_agents.each do |a|
261
+ if (options = agent_options(a)) && options[:identity] == agent_id
262
+ return a
263
+ end
264
+ end
265
+ nil
266
+ end
267
+
268
+ # Get options from agent's configuration file
269
+ #
270
+ # === Parameters
271
+ # agent_name(String):: Agent name
272
+ #
273
+ # === Return
274
+ # (Hash|nil):: Agent options with key names symbolized,
275
+ # or nil if file not accessible or empty
276
+ def self.load_cfg(agent_name)
277
+ if (file = cfg_file(agent_name, exists = true)) && File.readable?(file) && (cfg = YAML.load(IO.read(file)))
278
+ SerializationHelper.symbolize_keys(cfg)
279
+ end
280
+ end
281
+
282
+ # Write agent's configuration to file
283
+ #
284
+ # === Parameters
285
+ # agent_name(String):: Agent name
286
+ # cfg(Hash):: Configuration options
287
+ #
288
+ # === Return
289
+ # file(String):: Configuration file path name
290
+ def self.store_cfg(agent_name, cfg)
291
+ file = cfg_file(agent_name)
292
+ FileUtils.mkdir_p(File.dirname(file))
293
+ File.delete(file) if File.exists?(file)
294
+ File.open(file, 'w') do |fd|
295
+ fd.puts "# Created at #{Time.now}"
296
+ fd.write(YAML.dump(cfg))
297
+ end
298
+ file
299
+ end
300
+
301
+ # Path to directory containing agent process id files
302
+ #
303
+ # === Return
304
+ # (String):: Directory path name
305
+ def self.pid_dir
306
+ @pid_dir ||= Platform.filesystem.pid_dir
307
+ end
308
+
309
+ # Retrieve agent process id file
310
+ #
311
+ # === Parameters
312
+ # agent_name(String):: Agent name
313
+ #
314
+ # === Return
315
+ # (PidFile|nil):: Process id file, or nil if there is no configuration file for agent
316
+ def self.pid_file(agent_name)
317
+ if options = load_cfg(agent_name)
318
+ PidFile.new(options[:identity], options[:pid_dir])
319
+ end
320
+ end
321
+
322
+ # Agent options from generated agent configuration file
323
+ # and agent process id file if they exist
324
+ # Reset root_dir and pid_dir to one found in agent configuration file
325
+ #
326
+ # === Parameters
327
+ # agent_name(String):: Agent name
328
+ #
329
+ # === Return
330
+ # options(Hash):: Agent options including
331
+ # :identity(String):: Serialized agent identity
332
+ # :log_path(String):: Path to directory for agent log file
333
+ # :pid(Integer):: Agent process pid if available
334
+ # :listen_port(Integer):: Agent command listen port if available
335
+ # :cookie(String):: Agent command cookie if available
336
+ def self.agent_options(agent_name)
337
+ if options = load_cfg(agent_name)
338
+ @root_dirs = array(options[:root_dir])
339
+ @pid_dir = options[:pid_dir]
340
+ options[:log_path] = options[:log_dir] || Platform.filesystem.log_dir
341
+ pid_file = PidFile.new(options[:identity])
342
+ options.merge!(pid_file.read_pid) if pid_file.exists?
343
+ end
344
+ options || {}
345
+ end
346
+
347
+ protected
348
+
349
+ # Convert value to array if not an array, unless nil
350
+ def self.array(value)
351
+ (value.nil? || value.is_a?(Array)) ? value : [value]
352
+ end
353
+
354
+ # Ordered list of root directories
355
+ def self.root_dirs
356
+ @root_dirs || [Dir.pwd]
357
+ end
358
+
359
+ # Path to agent directory containing initialization files
360
+ def self.init_dir(root_dir)
361
+ File.normalize_path(File.join(root_dir, "init"))
362
+ end
363
+
364
+ # Path to directory containing actor source files
365
+ def self.actors_dir(root_dir)
366
+ File.normalize_path(File.join(root_dir, "actors"))
367
+ end
368
+
369
+ # Path to agent directory containing code
370
+ def self.lib_dir2(root_dir)
371
+ File.normalize_path(File.join(root_dir, "lib"))
372
+ end
373
+
374
+ # Path to agent directory containing scripts
375
+ def self.scripts_dir2(root_dir)
376
+ File.normalize_path(File.join(root_dir, "scripts"))
377
+ end
378
+
379
+ # All existing directories of given type
380
+ def self.all_dirs(type)
381
+ dirs = []
382
+ root_dirs.each do |d|
383
+ c = self.__send__(type, d)
384
+ dirs << c if File.directory?(c)
385
+ end
386
+ dirs
387
+ end
388
+
389
+ # Path name of first file found of given type and name, or nil if none found
390
+ def self.first_file(type, name)
391
+ file = nil
392
+ root_dirs.each do |d|
393
+ if File.exist?(f = File.join(self.__send__(type, d), name))
394
+ file = f
395
+ break
396
+ end
397
+ end
398
+ file
399
+ end
400
+
401
+ end # AgentConfig
402
+
403
+ end # RightScale
@@ -0,0 +1,209 @@
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
+ module RightScale
24
+
25
+ # Agent identity management
26
+ class AgentIdentity
27
+
28
+ # Cutover time at which agents began using new separator
29
+ SEPARATOR_EPOCH = Time.at(1256702400) unless defined?(SEPARATOR_EPOCH) # Tue Oct 27 21:00:00 -0700 2009
30
+
31
+ # Separator used to differentiate between identity components when serialized
32
+ ID_SEPARATOR = '-' unless defined?(ID_SEPARATOR)
33
+
34
+ # Separator used to differentiate between identity components prior to release 3.4
35
+ ID_SEPARATOR_OLD = '*' unless defined?(ID_SEPARATOR_OLD)
36
+
37
+ # Identity components
38
+ attr_reader :prefix, :agent_type, :token, :base_id
39
+
40
+ # Generate new id
41
+ #
42
+ # === Parameters
43
+ # prefix(String):: Prefix used to scope identity
44
+ # agent_type(String):: Agent type (e.g. 'core', 'instance')
45
+ # base_id(Integer):: Unique integer value
46
+ # token(String):: Unique identity token, will be generated randomly if not provided
47
+ # separator(String):: Character used to separate identity components, defaults to ID_SEPARATOR
48
+ #
49
+ # === Raise
50
+ # RightScale::Exceptions::Argument:: Invalid argument
51
+ def initialize(prefix, agent_type, base_id, token=nil, separator=nil)
52
+ err = "Prefix cannot contain '#{ID_SEPARATOR}'" if prefix && prefix.include?(ID_SEPARATOR)
53
+ err = "Prefix cannot contain '#{ID_SEPARATOR_OLD}'" if prefix && prefix.include?(ID_SEPARATOR_OLD)
54
+ err = "Agent type cannot contain '#{ID_SEPARATOR}'" if agent_type.include?(ID_SEPARATOR)
55
+ err = "Agent type cannot contain '#{ID_SEPARATOR_OLD}'" if agent_type.include?(ID_SEPARATOR_OLD)
56
+ err = "Agent type cannot be nil" if agent_type.nil?
57
+ err = "Agent type cannot be empty" if agent_type.size == 0
58
+ err = "Base ID must be a positive integer" unless base_id.kind_of?(Integer) && base_id >= 0
59
+ err = "Token cannot contain '#{ID_SEPARATOR}'" if token && token.include?(ID_SEPARATOR)
60
+ err = "Token cannot contain '#{ID_SEPARATOR_OLD}'" if token && token.include?(ID_SEPARATOR_OLD)
61
+ raise RightScale::Exceptions::Argument, err if err
62
+
63
+ @separator = separator || ID_SEPARATOR
64
+ @prefix = prefix
65
+ @agent_type = agent_type
66
+ @token = token || self.class.generate
67
+ @base_id = base_id
68
+ end
69
+
70
+ # Generate unique identity
71
+ #
72
+ # === Return
73
+ # id(String):: Random 128-bit hexadecimal string
74
+ def self.generate
75
+ bytes = Platform.rng.pseudorandom_bytes(16)
76
+ #Transform into hex string
77
+ id = bytes.unpack('H*')[0]
78
+ end
79
+
80
+ # Check validity of given serialized identity
81
+ #
82
+ # === Parameters
83
+ # serialized_id(String):: Serialized identity to be tested
84
+ #
85
+ # === Return
86
+ # (Boolean):: true if serialized identity is a valid, otherwise false
87
+ def self.valid?(serialized_id)
88
+ valid_parts?(self.compatible_serialized(serialized_id)) if serialized_id
89
+ end
90
+
91
+ # Instantiate by parsing given token
92
+ #
93
+ # === Parameters
94
+ # serialized_id(String):: Valid serialized agent identity (use 'valid?' to check first)
95
+ #
96
+ # === Return
97
+ # (AgentIdentity):: Corresponding agent identity
98
+ #
99
+ # === Raise
100
+ # (RightScale::Exceptions::Argument):: Serialized agent identity is incorrect
101
+ def self.parse(serialized_id)
102
+ serialized_id = self.compatible_serialized(serialized_id)
103
+ prefix, agent_type, token, bid, separator = parts(serialized_id)
104
+ raise RightScale::Exceptions::Argument, "Invalid agent identity token" unless prefix && agent_type && token && bid
105
+ base_id = bid.to_i
106
+ raise RightScale::Exceptions::Argument, "Invalid agent identity token (Base ID)" unless base_id.to_s == bid
107
+
108
+ AgentIdentity.new(prefix, agent_type, base_id, token, separator)
109
+ end
110
+
111
+ # Convert serialized agent identity to format valid for given protocol version
112
+ # Ignore identity that is not in serialized AgentIdentity format
113
+ #
114
+ # === Parameters
115
+ # serialized_id(String):: Serialized agent identity to be converted
116
+ # version(Integer):: Target protocol version
117
+ #
118
+ # === Return
119
+ # serialized_id(String):: Compatible serialized agent identity
120
+ def self.compatible_serialized(serialized_id, version = 10)
121
+ if version < 10
122
+ serialized_id = "nanite-#{serialized_id}" if self.valid_parts?(serialized_id)
123
+ else
124
+ serialized_id = serialized_id[7..-1] if serialized_id =~ /^nanite-|^mapper-/
125
+ end
126
+ serialized_id
127
+ end
128
+
129
+ # Check whether identity corresponds to an instance agent
130
+ #
131
+ # === Return
132
+ # (Boolean):: true if id corresponds to an instance agent, otherwise false
133
+ def instance_agent?
134
+ agent_type == 'instance'
135
+ end
136
+
137
+ # Check whether identity corresponds to an instance agent
138
+ #
139
+ # === Parameters
140
+ # serialized_id(String):: Valid serialized agent identity (use 'valid?' to check first)
141
+ #
142
+ # === Return
143
+ # (Boolean):: true if id corresponds to an instance agent, otherwise false
144
+ def self.instance_agent?(serialized_id)
145
+ parts(serialized_id)[1] == 'instance'
146
+ end
147
+
148
+ # String representation of identity
149
+ #
150
+ # === Return
151
+ # (String):: Serialized identity
152
+ def to_s
153
+ "#{@prefix}#{@separator}#{@agent_type}#{@separator}#{@token}#{@separator}#{@base_id}"
154
+ end
155
+
156
+ # Comparison operator
157
+ #
158
+ # === Parameters
159
+ # other(AgentIdentity):: Other agent identity
160
+ #
161
+ # === Return
162
+ # (Boolean):: true if other is identical to self, otherwise false
163
+ def ==(other)
164
+ other.kind_of?(::RightScale::AgentIdentity) &&
165
+ prefix == other.prefix &&
166
+ agent_type == other.agent_type &&
167
+ token == other.token &&
168
+ base_id == other.base_id
169
+ end
170
+
171
+ protected
172
+
173
+ # Split given serialized id into its parts
174
+ #
175
+ # === Parameters
176
+ # serialized_id(String):: Valid serialized agent identity (use 'valid?' to check first)
177
+ #
178
+ # === Return
179
+ # (Array):: Array of parts: prefix, agent type, token, base id and separator
180
+ def self.parts(serialized_id)
181
+ prefix = agent_type = token = bid = separator = nil
182
+ if serialized_id.include?(ID_SEPARATOR)
183
+ prefix, agent_type, token, bid = serialized_id.split(ID_SEPARATOR)
184
+ separator = ID_SEPARATOR
185
+ elsif serialized_id.include?(ID_SEPARATOR_OLD)
186
+ prefix, agent_type, token, bid = serialized_id.split(ID_SEPARATOR_OLD)
187
+ separator = ID_SEPARATOR_OLD
188
+ end
189
+ [ prefix, agent_type, token, bid, separator ]
190
+ end
191
+
192
+ # Check that given serialized identity has valid parts
193
+ #
194
+ # === Parameters
195
+ # serialized_id(String):: Serialized identity to be tested
196
+ #
197
+ # === Return
198
+ # (Boolean):: true if serialized identity is a valid identity token, otherwise false
199
+ def self.valid_parts?(serialized_id)
200
+ p = parts(serialized_id)
201
+ res = p.size == 5 &&
202
+ p[1] && p[1].size > 0 &&
203
+ p[2] && p[2].size > 0 &&
204
+ p[3] && p[3].to_i.to_s == p[3]
205
+ end
206
+
207
+ end # AgentIdentity
208
+
209
+ end # RightScale