Module: Beaker::Shared::HostManager

Included in:
Beaker::Shared
Defined in:
lib/beaker/shared/host_manager.rb

Overview

Methods for managing Hosts.

  • selecting hosts by role (Symbol or String)

  • selecting hosts by name (String)

  • adding additional method definitions for selecting by role

  • executing blocks of code against selected sets of hosts

Instance Method Summary collapse

Instance Method Details

#find_at_most_one_host_with_role(hosts, role) ⇒ Host

Find at most a single host with the role provided. Raise an error if more than one host is found to have the provided role.

Parameters:

  • hosts (Array<Host>)

    The hosts to examine

  • role (String)

    The host returned will have this role in its role list

Returns:

  • (Host)

    The single host with the desired role in its roles list or nil if no host is found

Raises:

  • (ArgumentError)

    Raised if more than one host has the given role defined, or if role = nil since hosts_with_role(nil) returns all hosts.



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/beaker/shared/host_manager.rb', line 59

def find_at_most_one_host_with_role(hosts, role)
  raise ArgumentError, "role cannot be nil." if role.nil?

  role_hosts = hosts_with_role(hosts, role)
  case role_hosts.length
  when 0
    nil
  when 1
    role_hosts[0]
  else
    host_string = (role_hosts.map { |host| host.name }).join(', ')
    raise ArgumentError, "There should be only one host with #{role} defined, but I found #{role_hosts.length} (#{host_string})"
  end
end

#hosts_with_name(hosts, name = nil) ⇒ Array<Host>

Find hosts from a given array of hosts that all have the desired name, match against host name, vmhostname and ip (the three valid ways to identify an individual host)

Parameters:

  • hosts (Array<Host>)

    The hosts to examine

  • name (String) (defaults to: nil)

    The hosts returned will have this name/vmhostname/ip

Returns:

  • (Array<Host>)

    The hosts that have the desired name/vmhostname/ip



24
25
26
27
28
# File 'lib/beaker/shared/host_manager.rb', line 24

def hosts_with_name(hosts, name = nil)
  hosts.select do |host|
    name.nil? or host.name&.start_with?(name) or host[:vmhostname]&.start_with?(name) or host[:ip]&.start_with?(name)
  end
end

#hosts_with_role(hosts, desired_role = nil) ⇒ Array<Host>

Find hosts from a given array of hosts that all have the desired role.

Parameters:

  • hosts (Array<Host>)

    The hosts to examine

  • desired_role (String) (defaults to: nil)

    The hosts returned will have this role in their roles list

Returns:

  • (Array<Host>)

    The hosts that have the desired role in their roles list



13
14
15
16
17
# File 'lib/beaker/shared/host_manager.rb', line 13

def hosts_with_role(hosts, desired_role = nil)
  hosts.select do |host|
    desired_role.nil? or host['roles'].include?(desired_role.to_s)
  end
end

#only_host_with_role(hosts, role) ⇒ Host

Find a single host with the role provided. Raise an error if more than one host is found to have the provided role.

Parameters:

  • hosts (Array<Host>)

    The hosts to examine

  • role (String)

    The host returned will have this role in its role list

Returns:

  • (Host)

    The single host with the desired role in its roles list

Raises:

  • (ArgumentError)

    Raised if more than one host has the given role defined, if no host has the role defined, or if role = nil since hosts_with_role(nil) returns all hosts.



37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/beaker/shared/host_manager.rb', line 37

def only_host_with_role(hosts, role)
  raise ArgumentError, "role cannot be nil." if role.nil?

  a_host = hosts_with_role(hosts, role)
  if a_host.length == 0
    raise ArgumentError, "There should be one host with #{role} defined!"
  elsif a_host.length > 1
    host_string = (a_host.map { |host| host.name }).join(', ')
    raise ArgumentError, "There should be only one host with #{role} defined, but I found #{a_host.length} (#{host_string})"
  end

  a_host.first
end

#run_block_on(hosts = [], filter = nil, opts = {}, &block) ⇒ Array<Result>, ...

TODO:

(beaker3.0:BKR-571): simplify return types to Array<Result> only

Execute a block selecting the hosts that match with the provided criteria

Parameters:

  • hosts (Array<Host>, Host) (defaults to: [])

    The host or hosts to run the provided block against

  • filter (String, Symbol) (defaults to: nil)

    Optional filter to apply to provided hosts - limits by name or role

  • opts (Hash{Symbol=>String}) (defaults to: {})
  • block (Block)

    This method will yield to a block of code passed by the caller

Options Hash (opts):

  • :run_in_parallel (Boolean)

    Whether to run on each host in parallel.

Returns:

  • (Array<Result>, Result, nil)

    If an array of hosts has been passed (after filtering), then either an array of results is returned (if the array is non-empty), or nil is returned (if the array is empty). Else, a result object is returned. If filtering makes it such that only one host is left, then it’s passed as a host object (not in an array), and thus a result object is returned.



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
# File 'lib/beaker/shared/host_manager.rb', line 90

def run_block_on hosts = [], filter = nil, opts = {}, &block
  result = nil
  block_hosts = hosts # the hosts to apply the block to after any filtering
  if filter
    raise ArgumentError, "Unable to sort for #{filter} type hosts when provided with [] as Hosts" if hosts.empty?

    block_hosts = hosts_with_role(hosts, filter) # check by role
    if block_hosts.empty?
      block_hosts = hosts_with_name(hosts, filter) # check by name
    end
    block_hosts = block_hosts.pop if block_hosts.length == 1 # we only found one matching host, don't need it wrapped in an array

  end
  if block_hosts.is_a? Array
    if block_hosts.length > 0
      if run_in_parallel? opts
        # Pass caller[1] - the line that called block_on - for logging purposes.
        result = block_hosts.map.each_in_parallel(caller(2..2).first) do |h|
          run_block_on h, &block
        end
        hosts.each { |host| host.close } # For some reason, I have to close the SSH connection
        # after spawning a process and running commands on a host,
        # or else it gets into a broken state for the next call.
      else
        result = block_hosts.map do |h|
          run_block_on h, &block
        end
      end
    elsif (cur_logger = (logger || @logger))
      # there are no matching hosts to execute against
      # should warn here
      # check if logger is defined in this context
      cur_logger.info "Attempting to execute against an empty array of hosts (#{hosts}, filtered to #{block_hosts}), no execution will occur"
    end
  else
    result = yield block_hosts
  end
  result
end