App-FuguVM
view release on metacpan or search on metacpan
lib/App/FuguVM/Remote.pm view on Meta::CPAN
package App::FuguVM::Remote;
our $VERSION = '0.2.0';
use File::Basename;
use File::Find ();
use Fugu::File;
use Fugu::Log;
use Fugu::SSH;
# App::FuguVM::Remote - the remote side of one running guest.
#
# The module owns the Fugu::SSH object, the argument quoting, the
# local walk, and the publish order. Thus App::FuguVM::CLI keeps thin
# command bodies, and one module holds the session timeout.
use constant {
# Seconds. Fugu::SSH bounds the connect and the channel read
# with this one value. The value holds a guest build and a
# large transfer, and it still ends a hang.
SSH_TIMEOUT => 3600,
# Bytes, for one file. write_file and read_file each hold a
# file in memory, so the cap fails closed.
MAX_TRANSFER_SIZE => 64 * 1024 * 1024,
# Paths for each batched remote command, so no command line
# grows too long.
BATCH_PATHS => 100,
};
# The caller must give a host and a port. A missing value is a
# programming error, because App::FuguVM::CLI resolves both before it
# builds the object.
sub new ( $class, %args )
{
die "App::FuguVM::Remote->new needs a host\n"
if !defined $args{host};
die "App::FuguVM::Remote->new needs a port\n"
if !defined $args{port};
my $self = bless {
host => $args{host},
port => $args{port},
ssh => Fugu::SSH->new(
host => $args{host},
port => $args{port},
user => 'root',
timeout => SSH_TIMEOUT,
),
}, $class;
return $self;
}
# $class_or_self->quote_argv(@argv):
# Return one remote command string. Each word gets single
# quotes, and a single quote inside a word becomes the '\''
# form. An empty word becomes ''. The words join with one
# space. So the remote shell splits the string at the word
# boundaries only: it expands nothing, and it globs nothing.
sub quote_argv ( $, @argv )
{
my @words;
for my $word (@argv) {
my $quoted = $word;
$quoted =~ s/'/'\\''/g;
push @words, "'$quoted'";
}
return join ' ', @words;
}
# $self->run(@argv):
# Run one argument vector on the guest. The method returns the
# hash of Fugu::SSH->run_command. An empty vector is a
# programming error.
sub run ( $self, @argv )
{
die "App::FuguVM::Remote->run needs an argument vector\n"
if !@argv;
return $self->{ssh}->run_command( $self->quote_argv(@argv) );
}
# $self->interactive:
# Open an interactive session. The method returns the exit code
# of ssh(1).
sub interactive ($self)
{
return $self->{ssh}->interactive;
}
# $self->put($local, $remote, %args):
# Copy a local file or a local directory to $remote in the
# guest. $remote is never a container: the content of a
# directory arrives under $remote, with no component for the
# source directory name. %args takes 'mode', the mode of each
# file that the method writes. Without it each file keeps its
# local permission bits, masked with 0777.
#
# The method creates every remote directory first, writes each
# file to a temporary name beside its destination, and then
# publishes every temporary file with mv -f. A failure removes
# every temporary file, so a failed run leaves no partial
# destination file. The method returns 1, or undef.
sub put ( $self, $local, $remote, %args )
{
my $entries = $self->_entries( $local, $remote, $args{mode} );
return if !defined $entries;
my @dirs = grep { $_->{type} eq 'dir' } @$entries;
my @files = grep { $_->{type} eq 'file' } @$entries;
return if @dirs && !$self->_mkdir( map { $_->{dest} } @dirs );
my @temps;
for my $entry (@files) {
my $temp = "$entry->{dest}.fuguvm.$$";
my $content = Fugu::File->read( $entry->{source} );
( run in 0.685 second using v1.01-cache-2.11-cpan-a49fcb8fa48 )