Rex

 view release on metacpan or  search on metacpan

lib/Rex/Commands/Fs.pm  view on Meta::CPAN

#
# (c) Jan Gehring <jan.gehring@gmail.com>
#

=head1 NAME

Rex::Commands::Fs - File system commands

=head1 DESCRIPTION

With this module you can do file system tasks like creating directories, deleting or moving files, and more.

=head1 SYNOPSIS

 my @files = list_files "/etc";

 unlink("/tmp/file");

 rmdir("/tmp");
 mkdir("/tmp");

 my %stat = stat("/etc/passwd");

 my $link = readlink("/path/to/a/link");
 symlink("/source", "/dest");

 rename("oldname", "newname");

 chdir("/tmp");

 is_file("/etc/passwd");
 is_dir("/etc");
 is_writeable("/tmp");
 is_writable("/tmp");

 chmod 755, "/tmp";
 chown "user", "/tmp";
 chgrp "group", "/tmp";


=head1 EXPORTED FUNCTIONS

=cut

package Rex::Commands::Fs;

use v5.14.4;
use warnings;

our $VERSION = '1.16.1'; # VERSION

require Rex::Exporter;
use Data::Dumper;
use Fcntl;
use Rex::Helper::File::Spec;
use Rex::Helper::SSH2;
use Rex::Helper::Path;
use Rex::Commands;
use Rex::Interface::Fs;
use Rex::Interface::Exec;
use Rex::Interface::File;
use File::Basename;
use Rex::Commands::MD5;

use vars qw(@EXPORT);
use base qw(Rex::Exporter);

@EXPORT = qw(list_files ls
  unlink rm rmdir mkdir stat readlink symlink ln rename mv chdir cd cp
  chown chgrp chmod
  is_file is_dir is_readable is_writeable is_writable is_symlink
  df du
  mount umount
  glob);

use vars qw(%file_handles);

=head2 Changing content

These commands are supposed to change the contents of the file system.

=head3 symlink($from, $to)

This function will create a symbolic link from C<$from> to C<$to>.

 task "symlink", "server01", sub {
   symlink("/var/www/versions/1.0.0", "/var/www/html");
 };

=cut

sub symlink {
  my ( $from, $to ) = @_;
  $from = resolv_path($from);
  $to   = resolv_path($to);

  Rex::get_current_connection()->{reporter}
    ->report_resource_start( type => "symlink", name => $to );

  my $fs = Rex::Interface::Fs->create;
  if ( $fs->is_symlink($to) && $fs->readlink($to) eq $from ) {
    Rex::get_current_connection()->{reporter}->report( changed => 0, );
  }
  else {
    $fs->ln( $from, $to ) or die("Can't link $from -> $to");
    Rex::get_current_connection()->{reporter}
      ->report( changed => 1, message => "Symlink created: $from -> $to." );
  }

  Rex::get_current_connection()->{reporter}
    ->report_resource_end( type => "symlink", name => $to );

  return 1;
}

=head3 ln($from, $to)

C<ln> is an alias for C<symlink>

=cut

sub ln {
  &symlink(@_);
}

=head3 unlink($file)

This function will remove the given C<$file>.

 task "unlink", "server01", sub {

lib/Rex/Commands/Fs.pm  view on Meta::CPAN


=back

With Rex-0.45 and newer, please use the L<file|Rex::Commands::File#file> resource instead.

 task "prepare", sub {
   file "/tmp",
     ensure => "directory",
     owner  => "root",
     group  => "root",
     mode   => 1777;
 };

Direct usage:

 task "mkdir", "server01", sub {
   mkdir "/tmp";

   mkdir "/tmp",
     owner => "root",
     group => "root",
     mode => 1777;
 };

=cut

sub mkdir {
  Rex::Logger::debug("Creating directory $_[0]");
  my $dir = shift;
  $dir = resolv_path($dir);

  my $options = {@_};

  $options->{on_change} //= sub { };

  Rex::get_current_connection()->{reporter}
    ->report_resource_start( type => "mkdir", name => $dir );

  my $fs = Rex::Interface::Fs->create;

  my $not_created = 0;
  my %old_stat;
  my $changed = 0;

  if ( $fs->is_dir($dir) ) {
    $not_created = 1;
    %old_stat    = &stat($dir);
  }

  my $mode          = $options->{"mode"}          || 755;
  my $owner         = $options->{"owner"}         || "";
  my $group         = $options->{"group"}         || "";
  my $not_recursive = $options->{"not_recursive"} || 0;

  if ($not_recursive) {
    if ( !$fs->mkdir($dir) ) {
      Rex::Logger::debug("Can't create directory $dir");
      die("Can't create directory $dir");
    }

    &chown( $owner, $dir ) if $owner;
    &chgrp( $group, $dir ) if $group;
    &chmod( $mode, $dir )  if $mode;
  }
  else {
    if ( !Rex::Helper::File::Spec->file_name_is_absolute($dir) ) {
      $dir = Rex::Helper::File::Spec->rel2abs($dir);
    }

    my @directories = __splitdir($dir);
    my $path_so_far = shift @directories;

    for my $part (@directories) {
      $path_so_far = Rex::Helper::File::Spec->join( $path_so_far, $part );

      if ( !is_dir($path_so_far) && !is_file($path_so_far) ) {
        if ( !$fs->mkdir($path_so_far) ) {
          Rex::Logger::debug("Can't create directory $dir");
          die("Can't create directory $dir");
        }

        &chown( $owner, $path_so_far ) if $owner;
        &chgrp( $group, $path_so_far ) if $group;
        &chmod( $mode, $path_so_far )  if $mode;
      }
    }
  }

  my %new_stat = &stat($dir);

  if ( !$not_created ) {
    Rex::get_current_connection()->{reporter}
      ->report( changed => 1, message => "Directory created." );
    $changed = 1;
  }

  if ( %old_stat && $old_stat{uid} != $new_stat{uid} ) {
    Rex::get_current_connection()->{reporter}
      ->report( changed => 1, message => "Owner updated." );
    $changed = 1;
  }

  if ( %old_stat && $old_stat{gid} != $new_stat{gid} ) {
    Rex::get_current_connection()->{reporter}
      ->report( changed => 1, message => "Group updated." );
    $changed = 1;
  }

  if ( %old_stat && $old_stat{mode} ne $new_stat{mode} ) {
    Rex::get_current_connection()->{reporter}
      ->report( changed => 1, message => "Mode updated." );
    $changed = 1;
  }

  if ( $changed == 0 ) {
    Rex::get_current_connection()->{reporter}->report( changed => 0, );
  }
  else {
    $options->{on_change}->($dir);
  }

  Rex::get_current_connection()->{reporter}
    ->report_resource_end( type => "mkdir", name => $dir );

  return 1;
}

sub __splitdir {
  return Rex::Helper::File::Spec->splitdir(shift);
}

=head3 chown($owner, $path)

Change the owner of a file or a directory.

 chown "www-data", "/var/www/html";

 chown "www-data", "/var/www/html",
                recursive => 1;


This command will not be reported.

If you want to use reports, please use the L<file|Rex::Commands::File#file> resource instead.

=cut

sub chown {
  my ( $user, $file, @opts ) = @_;

  $file = resolv_path($file);
  my $fs = Rex::Interface::Fs->create;
  $fs->chown( $user, $file, @opts ) or die("Can't chown $file");
}

=head3 chgrp($group, $path)

Change the group of a file or a directory.

 chgrp "nogroup", "/var/www/html";

 chgrp "nogroup", "/var/www/html",
              recursive => 1;


This command will not be reported.

If you want to use reports, please use the L<file|Rex::Commands::File#file> resource instead.

=cut

sub chgrp {
  my ( $group, $file, @opts ) = @_;
  $file = resolv_path($file);

  my $fs = Rex::Interface::Fs->create;
  $fs->chgrp( $group, $file, @opts ) or die("Can't chgrp $file");
}

=head3 chmod($mode, $path)

Change the permissions of a file or a directory.

 chmod 755, "/var/www/html";

 chmod 755, "/var/www/html",
          recursive => 1;


This command will not be reported.

If you want to use reports, please use the L<file|Rex::Commands::File#file> resource instead.

=cut

sub chmod {
  my ( $mode, $file, @opts ) = @_;
  $file = resolv_path($file);

  my $fs = Rex::Interface::Fs->create;
  $fs->chmod( $mode, $file, @opts ) or die("Can't chmod $file");
}

=head3 rename($old, $new)

This function will rename C<$old> to C<$new>. Will return 1 on success and 0 on failure.

 task "rename", "server01", sub {
   rename("/tmp/old", "/tmp/new");
 };

=cut



( run in 0.308 second using v1.01-cache-2.11-cpan-5511b514fd6 )