class RFacter::Node

Interface to a local or remote host

@note This class should be refacter to provide an abstracted interface to

different transport backends like Train, Vagrant, Chloride, etc.

@api public @since 0.1.0

Attributes

hostname[R]

@return [String]

id[R]

@return [String]

options[R]

@return [Hash]

password[R]

@return [String, nil]

port[R]

@return [Integer, nil]

scheme[R]

@return [String]

transport[R]
uri[R]

@return [URI]

user[R]

@return [String, nil]

Public Class Methods

new(uri, id: nil, config: RFacter::Config.config, **opts) click to toggle source

Returns a new instance of Node

@param uri [URI] The URI of the node. @param id [String, nil] An optional string to use when identifying

this node.
# File lib/rfacter/node.rb, line 48
def initialize(uri, id: nil, config: RFacter::Config.config, **opts)
  @config = config

  @uri = unless uri.is_a?(URI)
    URI.parse(uri.to_s)
  else
    uri
  end

  @hostname = @uri.hostname || @uri.path
  @scheme = if @uri.scheme.nil? && (@hostname == 'localhost')
    'local'
  elsif @uri.scheme.nil?
    'ssh'
  else
    @uri.scheme
  end

  case @scheme
  when 'ssh'
    @port = @uri.port || 22
    @user = @uri.user || 'root'
  when 'winrm'
    @user = @uri.user || 'Administrator'
  end

  @password = CGI.unescape(@uri.password) unless @uri.password.nil?
  @options = @uri.query.nil? ? Hash.new : CGI.parse(@uri.query)
  @options.update(opts)

  @id = unless id.nil?
          id
        else
          # Create a default from the URI, minus the password and options
          # components.
          id_string = "#{@scheme}://"
          id_string += "#{@user}@" unless @user.nil?
          id_string += @hostname
          id_string += ":#{@port}" unless @port.nil?
          id_string
        end

  @id.freeze

  # TODO: This should be abstracted.
  @transport = Train.create(@scheme,
    host: @hostname,
    user: @user,
    password: @password,
    port: @port,
    logger: logger, **@options)
end

Public Instance Methods

connection() click to toggle source

FIXME: For some reason, Train's connection re-use logic isn't working, so a new connection is being negotiated for each command. File a bug.

TODO: Ensure connection use is thread-safe.

# File lib/rfacter/node.rb, line 105
def connection
  @connection ||= @transport.connection
end
execute(command) click to toggle source

Execute a command on the node asynchronously

This method initiates the execution of a command line and returns an object representing the result.

@param command [String] The command string to execute.

@return [Train::Extras::CommandResult] The result of the command including

stdout, stderr and exit code.

@todo Add support for setting user accounts and environment variables.

# File lib/rfacter/node.rb, line 120
def execute(command)
  connection.run_command(command)
end
file(path) click to toggle source

Interact with remote files in a read-only manner

This method returns an object that can povide read only access to the stats and content of a particular file path.

@param path [String] The file path to interact with.

@return [Train::Extras::FileCommon] An object representing the remote file.

# File lib/rfacter/node.rb, line 156
def file(path)
  connection.file(path)
end
which(executable) click to toggle source

Determine if an executable exists and return the path

@param executable [String] The executable to locate.

@return [String] The path to the executable if it exists.

@return [nil] Returned when no matching executable can be located.

@todo Add support for setting user accounts and environment variables.

# File lib/rfacter/node.rb, line 133
def which(executable)
  # TODO: Abstract away from the Train "os" implementation.
  result = if connection.os.windows?
    connection.run_command("(Get-Command -TotalCount 1 #{executable}).Path")
  else
    connection.run_command("command -v #{executable}")
  end

  if (result.exit_status != 0) || (result.stdout.chomp.empty?)
    nil
  else
    result.stdout.chomp
  end
end