Class: HybridPlatformsConductor::NodesHandler
- Inherits:
-
Object
- Object
- HybridPlatformsConductor::NodesHandler
- Includes:
- LoggerHelpers, ParallelThreads
- Defined in:
- lib/hybrid_platforms_conductor/nodes_handler.rb
Overview
API to get information on our inventory: nodes and their metadata
Defined Under Namespace
Modules: ConfigDSLExtension Classes: GitError
Constant Summary
Constants included from LoggerHelpers
LoggerHelpers::LEVELS_MODIFIERS, LoggerHelpers::LEVELS_TO_STDERR
Instance Method Summary collapse
-
#define_property_method_for(property) ⇒ Object
Define a method to get a metadata property of a node.
-
#for_each_node_in(nodes, parallel: false, nbr_threads_max: nil, progress: 'Processing nodes') ⇒ Object
Iterate over a list of nodes.
-
#impacted_nodes_from_git_diff(platform_name, from_commit: 'master', to_commit: nil, smallest_set: false) ⇒ Object
Get the list of impacted nodes from a git diff on a platform.
-
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), config: Config.new, cmd_runner: CmdRunner.new, platforms_handler: PlatformsHandler.new) ⇒ NodesHandler
constructor
Constructor.
-
#invalidate_metadata_of(node, property) ⇒ Object
Invalidate a metadata property for a given node.
-
#known_nodes ⇒ Object
Get the list of known nodes.
-
#known_nodes_lists ⇒ Object
Get the list of known nodes lists.
-
#known_services ⇒ Object
Get the list of known service names.
-
#metadata_of(node, property = nil) ⇒ Object
Get a metadata property for a given node.
-
#method_missing(method, *args, &block) ⇒ Object
Accept any method of name get_<property>_of to get the metadata property of a given node.
-
#nodes_from_list(nodes_list, ignore_unknowns: false) ⇒ Object
Get the list of nodes (resolved) belonging to a nodes list.
-
#options_parse(options_parser, parallel: true) ⇒ Object
Complete an option parser with options meant to control this Nodes Handler.
-
#options_parse_nodes_selectors(options_parser, nodes_selectors) ⇒ Object
Complete an option parser with ways to select nodes in parameters.
-
#override_metadata_of(node, property, value) ⇒ Object
Override a metadata property for a given node.
-
#prefetch_metadata_of(nodes, properties) ⇒ Object
Prefetch some metadata properties for a given list of nodes.
-
#select_confs_for_node(node, configs) ⇒ Object
Select the configs applicable to a given node.
-
#select_confs_for_platform(platform_name, configs) ⇒ Object
Select the configs applicable to a given platform.
-
#select_from_nodes_selector_stack(nodes_selector_stack) ⇒ Object
Get the list of nodes impacted by a nodes selector stack.
-
#select_nodes(*nodes_selectors, ignore_unknowns: false) ⇒ Object
Resolve a list of nodes selectors into a real list of known nodes.
-
#sudo_on(node, user = 'root') ⇒ Object
Get the sudo command for a given user on a given node.
Methods included from LoggerHelpers
#err, #init_loggers, #log_component=, #log_debug?, #log_level=, #out, #section, #set_loggers_format, #stderr_device, #stderr_device=, #stderr_displayed?, #stdout_device, #stdout_device=, #stdout_displayed?, #stdouts_to_s, #with_progress_bar
Methods included from ParallelThreads
Constructor Details
#initialize(logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), config: Config.new, cmd_runner: CmdRunner.new, platforms_handler: PlatformsHandler.new) ⇒ NodesHandler
Constructor
- Parameters
-
logger (Logger): Logger to be used [default: Logger.new(STDOUT)]
-
logger_stderr (Logger): Logger to be used for stderr [default: Logger.new(STDERR)]
-
config (Config): Config to be used. [default: Config.new]
-
cmd_runner (CmdRunner): Command executor to be used. [default: CmdRunner.new]
-
platforms_handler (PlatformsHandler): Platforms Handler to be used. [default: PlatformsHandler.new]
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 76 def initialize( logger: Logger.new(STDOUT), logger_stderr: Logger.new(STDERR), config: Config.new, cmd_runner: CmdRunner.new, platforms_handler: PlatformsHandler.new ) init_loggers(logger, logger_stderr) @config = config @cmd_runner = cmd_runner @platforms_handler = platforms_handler # List of platform handler per known node # Hash<String, PlatformHandler> @nodes_platform = {} # List of platform handler per known nodes list # Hash<String, PlatformHandler> @nodes_list_platform = {} # List of CMDBs getting a property, per property name # Hash<Symbol, Array<Cmdb> > @cmdbs_per_property = {} # List of CMDBs having the get_others method # Array< Cmdb > @cmdbs_others = [] @cmdbs = Plugins.new( :cmdb, logger: @logger, logger_stderr: @logger_stderr, init_plugin: proc do |plugin_class| cmdb = plugin_class.new( logger: @logger, logger_stderr: @logger_stderr, config: @config, cmd_runner: @cmd_runner, platforms_handler: @platforms_handler, nodes_handler: self ) @cmdbs_others << cmdb if cmdb.respond_to?(:get_others) cmdb.methods.each do |method| if method.to_s =~ /^get_(.*)$/ property = $1.to_sym @cmdbs_per_property[property] = [] unless @cmdbs_per_property.key?(property) @cmdbs_per_property[property] << cmdb end end cmdb end ) # Cache of metadata per node # Hash<String, Hash<Symbol, Object> > @metadata = {} # The metadata update is protected by a mutex to make it thread-safe @metadata_mutex = Mutex.new # Cache of CMDB masters, per property, per node # Hash< String, Hash< Symbol, Cmdb > > @cmdb_masters_cache = {} # Read all platforms from the config @platforms_handler.known_platforms.each do |platform| # Register all known nodes for this platform platform.known_nodes.each do |node| raise "Can't register #{node} to platform #{platform.repository_path}, as it is already defined in platform #{@nodes_platform[node].repository_path}." if @nodes_platform.key?(node) @nodes_platform[node] = platform end # Register all known nodes lists platform.known_nodes_lists.each do |nodes_list| raise "Can't register nodes list #{nodes_list} to platform #{platform.repository_path}, as it is already defined in platform #{@nodes_list_platform[nodes_list].repository_path}." if @nodes_list_platform.key?(nodes_list) @nodes_list_platform[nodes_list] = platform end if platform.respond_to?(:known_nodes_lists) end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
Accept any method of name get_<property>_of to get the metadata property of a given node. Here is the magic of accepting method names that are not statically defined.
- Parameters
-
method (Symbol): The missing method name
-
args (Array<Object>): Arguments given to the call
-
block (Proc): Code block given to the call
330 331 332 333 334 335 336 337 338 339 340 341 342 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 330 def method_missing(method, *args, &block) if method.to_s =~ /^get_(.*)_of$/ property = $1.to_sym # Define the method so that we don't go trough method_missing next time (more efficient). define_property_method_for(property) # Then call it send("get_#{property}_of".to_sym, *args, &block) else # We really don't know this method. # Call original implementation of method_missing that will raise an exception. super end end |
Instance Method Details
#define_property_method_for(property) ⇒ Object
Define a method to get a metadata property of a node. This is like a factory of method shortcuts for properties. The method will be named get_<property>_of. This way instead of calling
node, :host_ip
we can call
get_host_ip_of node
Readability wins :D
- Parameters
-
property (Symbol): The property name
319 320 321 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 319 def define_property_method_for(property) define_singleton_method("get_#{property}_of".to_sym) { |node| (node, property) } end |
#for_each_node_in(nodes, parallel: false, nbr_threads_max: nil, progress: 'Processing nodes') ⇒ Object
Iterate over a list of nodes. Provide a mechanism to multithread this iteration (in such case the iterating code has to be thread-safe). In case of multithreaded run, a progress bar is being displayed.
- Parameters
-
nodes (Array<String>): List of nodes to iterate over
-
parallel (Boolean): Iterate in a multithreaded way? [default: false]
-
nbr_threads_max (Integer or nil): Maximum number of threads to be used in case of parallel, or nil for no limit [default: nil]
-
progress (String or nil): Name of a progress bar to follow the progression, or nil for no progress bar [default: ‘Processing nodes’]
-
Proc: The code called for each node being iterated on.
- Parameters
-
node (String): The node name
495 496 497 498 499 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 495 def for_each_node_in(nodes, parallel: false, nbr_threads_max: nil, progress: 'Processing nodes') for_each_element_in(nodes.sort, parallel: parallel, nbr_threads_max: nbr_threads_max, progress: progress) do |node| yield node end end |
#impacted_nodes_from_git_diff(platform_name, from_commit: 'master', to_commit: nil, smallest_set: false) ⇒ Object
Get the list of impacted nodes from a git diff on a platform
- Parameters
-
platform_name (String): The platform’s name
-
from_commit (String): Commit ID to check from [default: ‘master’]
-
to_commit (String or nil): Commit ID to check to, or nil for currently checked-out files [default: nil]
-
smallest_set (Boolean): Smallest set of impacted nodes? [default: false]
- Result
-
Array<String>: The list of nodes impacted by this diff (counting direct impacts, services and global files impacted)
-
Array<String>: The list of nodes directly impacted by this diff
-
Array<String>: The list of services impacted by this diff
-
Boolean: Are there some files that have a global impact (meaning all nodes are potentially impacted by this diff)?
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 513 def impacted_nodes_from_git_diff(platform_name, from_commit: 'master', to_commit: nil, smallest_set: false) platform = @platforms_handler.platform(platform_name) raise "Unkown platform #{platform_name}. Possible platforms are #{@platforms_handler.known_platforms.map(&:name).sort.join(', ')}" if platform.nil? begin _exit_status, stdout, _stderr = @cmd_runner.run_cmd "cd #{platform.repository_path} && git --no-pager diff --no-color #{from_commit} #{to_commit.nil? ? '' : to_commit}", log_to_stdout: log_debug? rescue CmdRunner::UnexpectedExitCodeError raise GitError, $!.to_s end # Parse the git diff output to create a structured diff # Hash< String, Hash< Symbol, Object > >: List of diffs info, per file name having a diff. Diffs info have the following properties: # * *moved_to* (String): The new file path, in case it has been moved [optional] # * *diff* (String): The diff content files_diffs = {} current_file_diff = nil stdout.split("\n").each do |line| case line when /^diff --git a\/(.+) b\/(.+)$/ # A new file diff from, to = $1, $2 current_file_diff = { diff: '' } current_file_diff[:moved_to] = to unless from == to files_diffs[from] = current_file_diff else current_file_diff[:diff] << "#{current_file_diff[:diff].empty? ? '' : "\n"}#{line}" unless current_file_diff.nil? end end impacted_nodes, impacted_services, impact_global = platform.impacts_from files_diffs impacted_services.sort! impacted_services.uniq! impacted_nodes.sort! impacted_nodes.uniq! [ if impact_global platform.known_nodes.sort else ( impacted_nodes + impacted_services.map do |service| service_nodes = select_nodes([{ service: service }]) smallest_set ? [service_nodes.first].compact : service_nodes end ).flatten.sort.uniq end, impacted_nodes, impacted_services, impact_global ] end |
#invalidate_metadata_of(node, property) ⇒ Object
Invalidate a metadata property for a given node
- Parameters
-
node (String): Node
-
property (Symbol): The property name
302 303 304 305 306 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 302 def (node, property) @metadata_mutex.synchronize do @metadata[node].delete(property) if @metadata.key?(node) end end |
#known_nodes ⇒ Object
Get the list of known nodes
- Result
-
Array<String>: List of nodes
236 237 238 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 236 def known_nodes @nodes_platform.keys end |
#known_nodes_lists ⇒ Object
Get the list of known nodes lists
- Result
-
Array<String>: List of nodes lists’ names
244 245 246 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 244 def known_nodes_lists @nodes_list_platform.keys end |
#known_services ⇒ Object
Get the list of known service names
- Result
-
Array<String>: List of service names
263 264 265 266 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 263 def known_services known_nodes, :services known_nodes.map { |node| get_services_of node }.flatten.compact.uniq.sort end |
#metadata_of(node, property = nil) ⇒ Object
Get a metadata property for a given node
- Parameters
-
node (String): Node
-
property (Symbol or nil): The property name, or nil for all [default=nil]
- Result
-
Object or nil: The node’s metadata value for this property, or nil if none, or a Hash of metadata if property was nil
275 276 277 278 279 280 281 282 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 275 def (node, property = nil) if property.nil? @metadata[node] || {} else ([node], property) unless @metadata.key?(node) && @metadata[node].key?(property) @metadata[node][property] end end |
#nodes_from_list(nodes_list, ignore_unknowns: false) ⇒ Object
Get the list of nodes (resolved) belonging to a nodes list
- Parameters
-
nodes_list (String): Nodes list name
-
ignore_unknowns (Boolean): Do we ignore unknown nodes? [default = false]
- Result
-
Array<String>: List of nodes
255 256 257 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 255 def nodes_from_list(nodes_list, ignore_unknowns: false) select_nodes(@nodes_list_platform[nodes_list].nodes_selectors_from_nodes_list(nodes_list), ignore_unknowns: ignore_unknowns) end |
#options_parse(options_parser, parallel: true) ⇒ Object
Complete an option parser with options meant to control this Nodes Handler
- Parameters
-
options_parser (OptionParser): The option parser to complete
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 150 def (, parallel: true) .separator '' .separator 'Nodes handler options:' .on('-o', '--show-nodes', 'Display the list of possible nodes and exit') do out "* Known platforms:\n#{ @platforms_handler.known_platforms.map do |platform| "#{platform.name} - Type: #{platform.platform_type} - Location: #{platform.repository_path}" end.sort.join("\n") }" out out "* Known nodes lists:\n#{known_nodes_lists.sort.join("\n")}" out out "* Known services:\n#{known_services.sort.join("\n")}" out out "* Known nodes:\n#{known_nodes.sort.join("\n")}" out out "* Known nodes with description:\n#{ known_nodes, %i[hostname host_ip private_ips services description] known_nodes.map do |node| "#{node} (#{ if get_hostname_of node get_hostname_of node elsif get_host_ip_of node get_host_ip_of node elsif get_private_ips_of node get_private_ips_of(node).first else 'No connection' end }) - #{(get_services_of(node) || []).join(', ')} - #{get_description_of(node) || ''}" end.sort.join("\n") }" out exit 0 end end |
#options_parse_nodes_selectors(options_parser, nodes_selectors) ⇒ Object
Complete an option parser with ways to select nodes in parameters
- Parameters
-
options_parser (OptionParser): The option parser to complete
-
nodes_selectors (Array): The list of nodes selectors that will be populated by parsing the options
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 192 def (, nodes_selectors) platform_names = @platforms_handler.known_platforms.map(&:name).sort .separator '' .separator 'Nodes selection options:' .on('-a', '--all-nodes', 'Select all nodes') do nodes_selectors << { all: true } end .on('-b', '--nodes-platform PLATFORM', "Select nodes belonging to a given platform name. Available platforms are: #{platform_names.join(', ')} (can be used several times)") do |platform| nodes_selectors << { platform: platform } end .on('-l', '--nodes-list LIST', 'Select nodes defined in a nodes list (can be used several times)') do |nodes_list| nodes_selectors << { list: nodes_list } end .on('-n', '--node NODE', 'Select a specific node. Can be a regular expression to select several nodes if used with enclosing "/" characters. (can be used several times).') do |node| nodes_selectors << node end .on('-r', '--nodes-service SERVICE', 'Select nodes implementing a given service (can be used several times)') do |service| nodes_selectors << { service: service } end .on( '--nodes-git-impact GIT_IMPACT', 'Select nodes impacted by a git diff from a platform (can be used several times).', 'GIT_IMPACT has the format PLATFORM:FROM_COMMIT:TO_COMMIT:FLAGS', "* PLATFORM: Name of the platform to check git diff from. Available platforms are: #{platform_names.join(', ')}", '* FROM_COMMIT: Commit ID or refspec from which we perform the diff. If ommitted, defaults to master', '* TO_COMMIT: Commit ID ot refspec to which we perform the diff. If ommitted, defaults to the currently checked-out files', '* FLAGS: Extra comma-separated flags. The following flags are supported:', ' - min: If specified then each impacted service will select only 1 node implementing this service. If not specified then all nodes implementing the impacted services will be selected.' ) do |nodes_git_impact| platform_name, from_commit, to_commit, flags = nodes_git_impact.split(':') flags = (flags || '').split(',') raise "Invalid platform in --nodes-git-impact: #{platform_name}. Possible values are: #{platform_names.join(', ')}." unless platform_names.include?(platform_name) nodes_selector = { platform: platform_name } nodes_selector[:from_commit] = from_commit if from_commit && !from_commit.empty? nodes_selector[:to_commit] = to_commit if to_commit && !to_commit.empty? nodes_selector[:smallest_set] = true if flags.include?('min') nodes_selectors << { git_diff: nodes_selector } end end |
#override_metadata_of(node, property, value) ⇒ Object
Override a metadata property for a given node
- Parameters
-
node (String): Node
-
property (Symbol): The property name
-
value (Object): The property value
290 291 292 293 294 295 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 290 def (node, property, value) @metadata_mutex.synchronize do @metadata[node] = {} unless @metadata.key?(node) @metadata[node][property] = value end end |
#prefetch_metadata_of(nodes, properties) ⇒ Object
Prefetch some metadata properties for a given list of nodes. Useful for performance reasons when clients know they will need to use a lot of properties on nodes. Keep a thread-safe memory cache of it.
- Parameters
-
nodes (Array<String>): Nodes to read metadata for
-
properties (Symbol or Array<Symbol>): Metadata properties (or single one) to read
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 351 def (nodes, properties) (properties.is_a?(Symbol) ? [properties] : properties).each do |property| # Gather the list of nodes missing this property missing_nodes = nodes.select { |node| !@metadata.key?(node) || !@metadata[node].key?(property) } unless missing_nodes.empty? # Query the CMDBs having first the get_<property> method, then the ones having the get_others method till we have our property set for all missing nodes # Metadata being retrieved by the different CMDBs, per node # Hash< String, Object > = {} ( (@cmdbs_per_property.key?(property) ? @cmdbs_per_property[property] : []).map { |cmdb| [cmdb, property] } + @cmdbs_others.map { |cmdb| [cmdb, :others] } ).each do |(cmdb, cmdb_property)| # If among the missing nodes some of them have some master CMDB declared for this property, filter them out unless we are dealing with their master CMDB. nodes_to_query = missing_nodes.select do |node| master_cmdb = cmdb_master_for(node, property) master_cmdb.nil? || master_cmdb == cmdb end unless nodes_to_query.empty? # Check first if this property depends on other ones for this cmdb if cmdb.respond_to?(:property_dependencies) property_deps = cmdb.property_dependencies nodes_to_query, property_deps[property] if property_deps.key?(property) end # Property values, per node name # Hash< String, Object > = Hash[ cmdb.send("get_#{cmdb_property}".to_sym, nodes_to_query, @metadata.slice(*nodes_to_query)).map do |node, cmdb_result| [node, cmdb_property == :others ? cmdb_result[property] : cmdb_result] end ].compact cmdb_log_header = "[CMDB #{cmdb.class.name.split('::').last}.#{cmdb_property}] -" log_debug "#{cmdb_log_header} Query property #{property} for #{nodes_to_query.size} nodes (#{nodes_to_query[0..7].join(', ')}...) => Found metadata for #{.size} nodes." .merge!() do |node, existing_value, new_value| raise "#{cmdb_log_header} Returned a conflicting value for metadata #{property} of node #{node}: #{new_value} whereas the value was already set to #{existing_value}" if !existing_value.nil? && new_value != existing_value new_value end end end # Avoid conflicts in metadata while merging and make sure this update is thread-safe # As @metadata is only appending data and never deleting it, protecting the update only is enough. # At worst several threads will query several times the same CMDBs to update the same data several times. # If we also want to be thread-safe in this regard, we should protect the whole CMDB call with mutexes, at the granularity of the node + property bein read. @metadata_mutex.synchronize do missing_nodes.each do |node| @metadata[node] = {} unless @metadata.key?(node) # Here, explicitely store nil if nothing has been found for a node because we know there is no value to be fetched. # This way we won't query again all CMDBs thanks to the cache. @metadata[node][property] = [node] end end end end end |
#select_confs_for_node(node, configs) ⇒ Object
Select the configs applicable to a given node.
- Parameters
-
node (String): The node for which we select configurations
-
configs (Array< Hash<Symbol,Object> >): Configuration properties. Each configuration is selected based on the nodes_selectors_stack property.
- Result
-
Array< Hash<Symbol,Object> >: The selected configurations
570 571 572 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 570 def select_confs_for_node(node, configs) configs.select { |config_info| select_from_nodes_selector_stack(config_info[:nodes_selectors_stack]).include?(node) } end |
#select_confs_for_platform(platform_name, configs) ⇒ Object
Select the configs applicable to a given platform.
- Parameters
-
platform_name (String): The platform for which we select configurations
-
configs (Array< Hash<Symbol,Object> >): Configuration properties. Each configuration is selected based on the nodes_selectors_stack property.
- Result
-
Array< Hash<Symbol,Object> >: The selected configurations
581 582 583 584 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 581 def select_confs_for_platform(platform_name, configs) platform_nodes = @platforms_handler.platform(platform_name).known_nodes configs.select { |config_info| (platform_nodes - select_from_nodes_selector_stack(config_info[:nodes_selectors_stack])).empty? } end |
#select_from_nodes_selector_stack(nodes_selector_stack) ⇒ Object
Get the list of nodes impacted by a nodes selector stack. The result is the intersection of every nodes set in the stack.
- Parameters
-
nodes_selector_stack (Array): The nodes selector stack
- Result
-
Array<String>: List of nodes
593 594 595 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 593 def select_from_nodes_selector_stack(nodes_selector_stack) nodes_selector_stack.inject(known_nodes) { |selected_nodes, nodes_selector| selected_nodes & select_nodes(nodes_selector) } end |
#select_nodes(*nodes_selectors, ignore_unknowns: false) ⇒ Object
Resolve a list of nodes selectors into a real list of known nodes. A node selector can be:
-
String: Node name, or a node regexp if enclosed within ‘/’ character (ex: ‘/.
worker.
/’) -
Hash<Symbol,Object>: More complete information that can contain the following keys:
-
all (Boolean): If true, specify that we want all known nodes.
-
list (String): Name of a nodes list.
-
platform (String): Name of a platform containing nodes.
-
service (String): Name of a service implemented by nodes.
-
git_diff (Hash<Symbol,Object>): Info about a git diff that impacts nodes:
-
platform (String): Name of the platform on which checking the git diff
-
from_commit (String): Commit ID to check from [default: ‘master’]
-
to_commit (String or nil): Commit ID to check to, or nil for currently checked-out files [default: nil]
-
smallest_set (Boolean): Smallest set of impacted nodes? [default: false]
-
-
- Parameters
-
nodes_selectors (Array<Object>): List of node selectors (can be a single element).
-
ignore_unknowns (Boolean): Do we ignore unknown nodes? [default = false]
- Result
-
Array<String>: List of nodes
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 425 def select_nodes(*nodes_selectors, ignore_unknowns: false) nodes_selectors = nodes_selectors.flatten # 1. Check for the presence of all return known_nodes if nodes_selectors.any? { |nodes_selector| nodes_selector.is_a?(Hash) && nodes_selector.key?(:all) && nodes_selector[:all] } # 2. Expand the nodes lists, platforms and services contents string_nodes = [] nodes_selectors.each do |nodes_selector| if nodes_selector.is_a?(String) string_nodes << nodes_selector else if nodes_selector.key?(:list) platform = @nodes_list_platform[nodes_selector[:list]] raise "Unknown nodes list: #{nodes_selector[:list]}" if platform.nil? string_nodes.concat(platform.nodes_selectors_from_nodes_list(nodes_selector[:list])) end string_nodes.concat(@platforms_handler.platform(nodes_selector[:platform]).known_nodes) if nodes_selector.key?(:platform) if nodes_selector.key?(:service) known_nodes, :services string_nodes.concat(known_nodes.select { |node| (get_services_of(node) || []).include?(nodes_selector[:service]) }) end if nodes_selector.key?(:git_diff) # Default values git_diff_info = { from_commit: 'master', to_commit: nil, smallest_set: false }.merge(nodes_selector[:git_diff]) all_impacted_nodes, _impacted_nodes, _impacted_services, _impact_global = impacted_nodes_from_git_diff( git_diff_info[:platform], from_commit: git_diff_info[:from_commit], to_commit: git_diff_info[:to_commit], smallest_set: git_diff_info[:smallest_set] ) string_nodes.concat(all_impacted_nodes) end end end # 3. Expand the Regexps real_nodes = [] string_nodes.each do |node| if node =~ /^\/(.+)\/$/ node_regexp = Regexp.new($1) real_nodes.concat(known_nodes.select { |known_node| known_node[node_regexp] }) else real_nodes << node end end # 4. Sort them unique real_nodes.uniq! real_nodes.sort! # Some sanity checks unless ignore_unknowns unknown_nodes = real_nodes - known_nodes raise "Unknown nodes: #{unknown_nodes.join(', ')}" unless unknown_nodes.empty? end real_nodes end |
#sudo_on(node, user = 'root') ⇒ Object
Get the sudo command for a given user on a given node
- Parameters
-
node (String): Node on which we need sudo
-
user (String): User for which we need sudo [default = ‘root’]
- Result
-
String: The corresponding sudo string
604 605 606 607 608 609 610 |
# File 'lib/hybrid_platforms_conductor/nodes_handler.rb', line 604 def sudo_on(node, user = 'root') sudo = nil select_confs_for_node(node, @config.sudo_procs).each do |sudo_proc_info| sudo = sudo_proc_info[:sudo_proc].call(user) end sudo.nil? ? "sudo -u #{user}" : sudo end |