Class: RobotArmy::TaskMaster

Inherits:
Thor
  • Object
show all
Defined in:
lib/robot-army/task_master.rb

Overview

The place where the magic happens

Types (shortcuts for use in this file)

HostList

<Array, String, nil>

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ TaskMaster

Returns a new instance of TaskMaster.



7
8
9
10
# File 'lib/robot-army/task_master.rb', line 7

def initialize(*args)
  super
  @dep_loader = DependencyLoader.new
end

Class Method Details

.host(host = nil) ⇒ String, :localhost

Gets or sets a single host that instances of RobotArmy::TaskMaster subclasses will use.

Parameters:

  • host (String, :localhost) (defaults to: nil)

    The fully-qualified domain name to connect to.

Returns:

  • (String, :localhost)

    The current value for the host.

Raises:

  • RobotArmy::HostArityError If you’re using the getter form of this method and you’ve already set multiple hosts, an error will be raised.



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/robot-army/task_master.rb', line 24

def self.host(host=nil)
  if host
    @hosts = nil
    @host = host
  elsif @hosts
    raise RobotArmy::HostArityError, 
      "There are #{@hosts.size} hosts, so calling host doesn't make sense"
  else
    @host
  end
end

.hosts(hosts = nil) ⇒ Array[String]

Gets or sets the hosts that instances of RobotArmy::TaskMaster subclasses will use.

Parameters:

  • hosts (Array[String]) (defaults to: nil)

    A list of fully-qualified domain names to connect to.

Returns:

  • (Array[String])

    The current list of hosts.



44
45
46
47
48
49
50
51
52
53
# File 'lib/robot-army/task_master.rb', line 44

def self.hosts(hosts=nil)
  if hosts
    @host = nil
    @hosts = hosts
  elsif @host
    [@host]
  else
    @hosts || []
  end
end

Instance Method Details

#connection(host) ⇒ Object

Gets an open connection for the host this instance is configured to use.

Returns:

  • RobotArmy::Connection An open connection with an active Ruby process.



115
116
117
# File 'lib/robot-army/task_master.rb', line 115

def connection(host)
  RobotArmy::GateKeeper.shared_instance.connect(host)
end

#cptemp(path, hosts = self.hosts, options = {}) {|path| ... } ⇒ Array<String>

Copies path to a temporary directory on each host.

Parameters:

  • path (String)

    A local file to copy.

  • hosts (HostList) (defaults to: self.hosts)

    Which hosts to connect to.

Yields:

  • (path)

    Yields the path of the newly copied file on each remote host.

Yield Parameters:

  • path (String)

    The path of the file under in a new directory under a temporary directory on the remote host.

Returns:

  • (Array<String>)

    An array of destination paths.



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/robot-army/task_master.rb', line 229

def cptemp(path, hosts=self.hosts, options={}, &block)
  hosts, options = self.hosts, hosts if hosts.is_a?(Hash)
  
  results = remote(hosts, options) do
    File.join(%x{mktemp -d -t robot-army.XXXX}.chomp, File.basename(path))
  end
  
  me = ENV['USER']
  host_and_path = Array(hosts).zip(Array(results))
  # copy them over
  host_and_path.each do |host, tmp|
    sudo(host) { FileUtils.chown(me, nil, File.dirname(tmp)) } if options[:user]
    scp path, tmp, host
    sudo(host) { FileUtils.chown(options[:user], nil, File.dirname(tmp)) } if options[:user]
  end
  # call the block on each host
  results = host_and_path.map do |host, tmp|
    remote(host, options.merge(:args => [tmp]), &block)
  end if block
  
  results.size == 1 ? results.first : results
end

#dependency(dep, ver = nil) ⇒ Object

Add a gem dependency this TaskMaster checks for on each remote host.

Parameters:

  • dep (String)

    The name of the gem to check for.

  • ver (String) (defaults to: nil)

    The version string of the gem to check for.



260
261
262
# File 'lib/robot-army/task_master.rb', line 260

def dependency(dep, ver = nil)
  @dep_loader.add_dependency dep, ver
end

#hostString, :localhost

Gets the first host for this instance of RobotArmy::TaskMaster.

Returns:

  • (String, :localhost)

    The host value to use.

Raises:

  • RobotArmy::HostArityError If you’re using the getter form of this method and you’ve already set multiple hosts, an error will be raised.



64
65
66
67
68
69
70
71
72
73
# File 'lib/robot-army/task_master.rb', line 64

def host
  if @host
    @host
  elsif @hosts
    raise RobotArmy::HostArityError, 
      "There are #{@hosts.size} hosts, so calling host doesn't make sense"
  else
    self.class.host
  end
end

#host=(host) ⇒ Object

Sets a single host for this instance of RobotArmy::TaskMaster.

Parameters:

  • host (String, :localhost)

    The host value to use.



80
81
82
83
# File 'lib/robot-army/task_master.rb', line 80

def host=(host)
  @hosts = nil
  @host = host
end

#hostsArray[String]

Gets the hosts for the instance of RobotArmy::TaskMaster.

Returns:

  • (Array[String])

    A list of hosts.



90
91
92
93
94
95
96
97
98
# File 'lib/robot-army/task_master.rb', line 90

def hosts
  if @hosts
    @hosts
  elsif @host
    [@host]
  else
    self.class.hosts
  end
end

#hosts=(hosts) ⇒ Object

Sets the hosts for this instance of RobotArmy::TaskMaster.

Parameters:

  • hosts (Array[String])

    A list of hosts.



105
106
107
108
# File 'lib/robot-army/task_master.rb', line 105

def hosts=(hosts)
  @host = nil
  @hosts = hosts
end

#remote(hosts = self.hosts, options = {}, &proc) ⇒ Object

Runs a block of Ruby on the machine specified by a host string and returns the return value of the block. Example:

remote { "foo" } # => "foo"

Local variables accessible from the block are also passed along to the remote process:

foo = "bar"
remote { foo } # => "bar"

Objects which can’t be marshalled, such as IO streams, will be proxied instead:

file = File.open("README.markdown", "r")
remote { file.gets } # => "Robot Army\n"

Parameters:

  • hosts (HostList) (defaults to: self.hosts)

    Which hosts to run the block on.

Returns:

  • (Object)

    Whatever is returned by the block.

Raises:

  • Exception Whatever is raised by the block.



177
178
179
180
181
# File 'lib/robot-army/task_master.rb', line 177

def remote(hosts=self.hosts, options={}, &proc)
  options, hosts = hosts, self.hosts if hosts.is_a?(Hash)
  results = Array(hosts).map {|host| remote_eval({:host => host}.merge(options), &proc) }
  results.size == 1 ? results.first : results
end

#scp(src, dest, hosts = self.hosts) ⇒ Object

Copies src to dest on each host.

Parameters:

  • src (String)

    A local file to copy.

  • dest (String)

    The path of a remote file to copy to.

Raises:

  • Errno::EACCES If the destination path cannot be written to.

  • Errno::ENOENT If the source path cannot be read.



197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/robot-army/task_master.rb', line 197

def scp(src, dest, hosts=self.hosts)
  Array(hosts).each do |host|
    output = `scp -q #{src} #{"#{host}:" unless host == :localhost}#{dest} 2>&1`
    case output
    when /Permission denied/i
      raise Errno::EACCES, output.chomp
    when /No such file or directory/i
      raise Errno::ENOENT, output.chomp
    end unless $?.exitstatus == 0
  end
  
  return nil
end

#sudo(hosts = self.hosts, options = {}, &proc) ⇒ Object

Runs a block of Ruby on the machine specified by a host string as root and returns the return value of the block. Example:

sudo { `shutdown -r now` }

You may also specify a user other than root. In this case sudo is the same as remote:

sudo(:user => 'www-data') { `/etc/init.d/apache2 restart` }

Parameters:

  • host (String, :localhost)

    The fully-qualified domain name of the machine to connect to, or :localhost if you want to use the same machine.

Returns:

  • (Object)

    Whatever is returned by the block.

Raises:

  • Exception Whatever is raised by the block.

See Also:



143
144
145
146
# File 'lib/robot-army/task_master.rb', line 143

def sudo(hosts=self.hosts, options={}, &proc)
  options, hosts = hosts, self.hosts if hosts.is_a?(Hash)
  remote hosts, {:user => 'root'}.merge(options), &proc
end