Module: Assemblr::Remote

Defined in:
lib/assemblr/remote.rb

Overview

Defines methods to handle interacting with remote nodes.

Defined Under Namespace

Modules: Group

Constant Summary collapse

@@nodes =
[]

Class Method Summary collapse

Class Method Details

.current_nodesArray<Hash{ip=>String, group=>String, user=>String}>

Return all nodes within the current default group.

Returns:

  • (Array<Hash{ip=>String, group=>String, user=>String}>)


102
103
104
105
106
# File 'lib/assemblr/remote.rb', line 102

def current_nodes
  return nodes if get_option(:remote_default_group) == 'all'

  @@nodes.select { |n| n[:group] == get_option(:remote_default_group).to_s }
end

.exec(ip, user, cmd, inject: []) ⇒ String

Execute a command on a remote machine using SSH. It returns the combined stdout and stderr data.

Parameters:

  • ip (String)

    the ip of the node to run the command on

  • user (String)

    the user to log in as

  • cmd (String)

    the command to execute remotely

  • inject (Array<String>) (defaults to: [])

    strings to inject into stdin

Returns:

  • (String)


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/assemblr/remote.rb', line 58

def exec(ip, user, cmd, inject: [])
  log_info "attempting to execute `#{cmd}` on #{user}@#{ip}"

  result = ''
  exit_code = 0
  Net::SSH.start(ip, user) do |ssh|
    ch = ssh.open_channel do |ch|
      ch.exec(cmd) do |ch, success|
        log_error "unable to execute `#{cmd}` on #{user}@#{ip}" unless success

        # Collect stdout data.
        ch.on_data do |_, data|
          result += data if data.is_a?(String)
        end

        # Collect stderr data.
        ch.on_extended_data do |_, _, data|
          result += data if data.is_a?(String)
        end

        inject.each do |line|
          ch.send_data(line + "\n")
        end

        ch.on_request 'exit-status' do |_, data|
          exit_code = data.read_long
          if exit_code.zero?
            log_success "`#{cmd}` executed successfully on #{user}@#{ip}"
          else
            log_error "`#{cmd}` returned status code #{exit_code} on #{user}@#{ip}"
          end
        end
      end
    end

    ch.wait
  end
  [result, exit_code]
end

.group(group, &block) ⇒ void

This method returns an undefined value.

Temporarily change the default group to refer to the specified group while within the passed block.

This method also changes the behavior of ‘remote_exec` and `remote_upload` while within the given block. Both functions don’t take an IP # and a user, as they collect the information for all nodes within the group. In that case, the return value is an array of the results given by each node.

Parameters:

  • group (Symbol)

    the group to temporarily switch to



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/assemblr/remote.rb', line 121

def group(group, &block)
  log_info %(entered group block with group "#{group}")

  # Temporarily switch to the target group.
  old_default_group = get_option(:remote_default_group)
  set_option(:remote_default_group, group.to_s)

  Group.class_eval(&block)

  set_option(:remote_default_group, old_default_group)

  log_info %(exited group block with group "#{group}")
end

.included(_) ⇒ Object



18
19
20
21
22
23
24
25
# File 'lib/assemblr/remote.rb', line 18

def self.included(_)
  expose_method :group
  expose_method :upload
  expose_method :exec
  expose_method :current_nodes
  expose_method :node
  expose_method :nodes
end

.node(ip, group: get_option(:remote_default_group), user: get_option(:remote_default_user)) ⇒ Hash{ip=>String, group=>String, user=>String}

Add a node to the list of nodes.

Parameters:

  • ip (String)

    the ip address of the node

  • group (Symbol) (defaults to: get_option(:remote_default_group))

    the group to assign the node to

  • user (String) (defaults to: get_option(:remote_default_user))

    the user to associate with the node

Returns:

  • (Hash{ip=>String, group=>String, user=>String})

    the added node



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/assemblr/remote.rb', line 167

def node(ip,
         group: get_option(:remote_default_group),
         user: get_option(:remote_default_user))
  group = group.to_s
  if group == 'all'
    message = 'nodes can not be assigned to the "all" group'
    error = ArgumentError.new(message)
    raise error
  end

  l_node = { ip: ip, group: group, user: user }
  @@nodes << l_node
  log_info "added node: #{l_node}"

  l_node
end

.nodesArray<Hash{ip=>String, group=>String, user=>String}>

Get all registered nodes.

Returns:

  • (Array<Hash{ip=>String, group=>String, user=>String}>)

    <Paste>



188
189
190
# File 'lib/assemblr/remote.rb', line 188

def nodes
  @@nodes
end

.upload(ip, user, src, dest, recursive: false, timeout: 5) ⇒ void

This method returns an undefined value.

Upload a file or directory to a node.

Parameters:

  • ip (String)

    the node to upload to

  • user (String)

    the user to authenticate as

  • src (String)

    the file to upload

  • dest (String)

    the location to upload to

  • recursive (Bool) (defaults to: false)

    recursively upload files in a directory

  • timeout (Integer) (defaults to: 5)

    how many seconds to wait before throwing an error



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/assemblr/remote.rb', line 146

def upload(ip, user, src, dest, recursive: false, timeout: 5)
  log_info %(attempting to upload "#{src}" to #{user}@#{ip}:#{dest})
  Timeout.timeout timeout do
    Net::SCP.upload!(ip, user, src, dest, recursive: recursive)
  end
rescue Timeout::Error
  log_error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest} within #{timeout} seconds)
rescue StandardError
  log_error %(failed to upload "#{src}" to #{user}@#{ip}:#{dest})
else
  log_success %(uploaded "#{src}" to #{user}@#{ip}:#{dest})
end