From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/env perl
#
# (c) Jan Gehring <jan.gehring@gmail.com>
#
use v5.12.5;
our $VERSION = '1.14.3.1'; # TRIAL VERSION
BEGIN {
# this is for new package format
if ( -d '/usr/lib/rex/lib' ) {
use lib '/usr/lib/rex/lib';
}
}
$|++;
use YAML;
use Cwd qw(getcwd);
use Carp;
use URI;
require Rex;
$Rex::Logger::silent = 1;
my ($major_minor) = ( $Rex::VERSION =~ m/^(\d*\.\d*)/ );
my $opts = {};
######
# default server
my $SEARCH_SERVER = "http://modules.rexify.org/api/$major_minor/get/recipes";
my $RECIPE_SERVER = "http://modules.rexify.org/api/$major_minor/get/mod/%s";
my $DEPEND_SERVER = "http://modules.rexify.org/api/$major_minor/get/dep/%s";
my $PERL_DEPEND_SERVER =
"http://modules.rexify.org/api/$major_minor/get/perldep/%s";
my $AUTH_USER;
my $AUTH_PASSWORD;
my $AUTH_REALM;
Rex::Config->register_config_handler(
"module_server",
sub {
my ($param) = @_;
if ( exists $param->{search} ) {
$SEARCH_SERVER = $param->{search};
}
if ( exists $param->{recipes} ) {
$RECIPE_SERVER = $param->{recipes};
}
if ( exists $param->{dependencies} ) {
$DEPEND_SERVER = $param->{dependencies};
}
if ( exists $param->{perl_dependencies} ) {
$PERL_DEPEND_SERVER = $param->{perl_dependencies};
}
if ( exists $param->{username} ) {
$AUTH_USER = $param->{username};
}
if ( exists $param->{password} ) {
$AUTH_PASSWORD = $param->{password};
}
if ( exists $param->{realm} ) {
$AUTH_REALM = $param->{realm};
}
}
);
for ( my $i = 0 ; $i < @ARGV ; $i++ ) {
if ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)=/ ) {
my $key = $1;
my ( $c_key, $val ) = split( /=/, $ARGV[$i], 2 );
if ( exists $opts->{$key} ) {
$opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} );
push( @{ $opts->{$key} }, $val );
}
else {
$opts->{$key} = $val || 0;
}
}
elsif ( $ARGV[$i] =~ m/^\-\-([a-z0-9\-_]+)/ ) {
my $key = $1;
if ( !$ARGV[ $i + 1 ] || $ARGV[ $i + 1 ] =~ m/^\-\-/ ) {
$opts->{$key} = 1;
}
else {
if ( exists $opts->{$key} ) {
$opts->{$key} = [ $opts->{$key} ] if ( !ref $opts->{$key} );
push( @{ $opts->{$key} }, $ARGV[ ++$i ] );
}
else {
$opts->{$key} = $ARGV[ ++$i ];
}
}
}
}
if ( !$ARGV[0]
|| join( ",", @ARGV ) =~ m/\-h,|\-\-help,/
|| $ARGV[0] eq "--help" )
{
print STDERR "Usage: rexify [<project-name> [<directory>]] [<options>]\n";
print STDERR "\n";
print STDERR "Options:";
print STDERR "\n";
print STDERR "\t--search=value\t\t\tWill search community recipes\n";
print STDERR "\t--use=recipe\t\t\tWill download community recipe\n";
print STDERR
"\t--init=git-url\t\t\tWill download and initialize the given repo\n";
print STDERR "\t--update-from-git\t\tUpdate the cloned repository.\n";
print STDERR
"\t--template=template\t\tUse a custom template to create the Rexfile skeleton\n";
print STDERR "\t--create-module\t\t\tCreate a module skeleton.\n";
print STDERR
"\t--create-module=Mod::Name\tCreate a module skeleton inside a Rex project.\n";
print STDERR "\t--sudo\t\t\t\tTo use sudo for Perl Module installation.\n";
print STDERR
"\t--resolve-deps\t\t\tRead meta.yml and try to resolve project dependencies\n";
print STDERR
"\t--no-install-perl-deps\t\tUse this if you don't want that rexify tries to install Perl Modules.\n";
print STDERR "\n";
print STDERR "Custom Templates:\n";
print STDERR " box - Template to use for Rex::Commands::Box projects.\n";
print STDERR "\n";
print STDERR "Application Bundle Commands:\n";
print STDERR "\n";
print STDERR
"\t--bundle\tCreate an all-containing redistributable binary application.\n";
print STDERR
"\t--task=taskname\tWhich task should be executed. default: setup\n";
print STDERR "\n";
print STDERR "Rex-JobControl Commands:\n";
print STDERR "\n";
print STDERR "\t--upload\tUpload Rexfile to JobControl server.\n";
print STDERR
"\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n";
print STDERR "\t\t--name=<name>\t\t\tThe name for the Rexfile.\n";
print STDERR
"\t\t--description='<description>'\tA small description for the Rexfile.\n";
print STDERR "\n";
print STDERR "\t--execute\tExecute Job on JobControl server.\n";
print STDERR
"\t\t--project=<project>\t\tProject where the Rexfile should be registered.\n";
print STDERR "\t\t--job=<job>\t\t\tThe name of the job to execute.\n";
print STDERR
"\t\t--hosts='<server list>'\tA comma seperated list of servers.\n";
print STDERR "\n";
print STDERR "General options:";
print STDERR "\n";
print STDERR "\t--server=<server>\t\tThe URL to JobControl server.\n";
print STDERR
"\t--username=<username>\t\tThe username to use for login to JobControl server.\n";
print STDERR "\t--password=<password>\t\tThe password for the user.\n";
print STDERR "\n";
exit 1;
}
sub print_found {
my ( $name, $data ) = @_;
$name =~ s/\//::/g;
$name =~ s/\.pm$//;
print "* $name\n";
print " Author : " . $data->{Author} . "\n";
print " Requires : " . join( ", ", @{ $data->{Requires} } ) . "\n"
if ( $data->{Requires} );
print " License : " . $data->{License} . "\n" if ( $data->{License} );
print " Description: " . $data->{Description} . "\n";
}
sub update_from_git {
system "git pull origin";
resolve_deps("meta.yml");
}
sub download_recipe_git {
my ($url) = @_;
$Rex::Logger::silent = 0;
my $u = URI->new($url);
my $branch = $u->query_param("branch");
my @splitted_path = split /\//, $u->path;
$splitted_path[-1] =~ s/\.git$//;
$branch ||= "master";
my $path = File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
$splitted_path[-1] );
my $parent_path = dirname $path;
mkdir $parent_path;
my $clone_url = $u->scheme . '://' . $u->host . $u->path;
Rex::Logger::info("Cloning $clone_url to $path. Using branch: $branch.");
system "git", "clone", $url, "-b", $branch, $path;
chdir $path;
resolve_deps("meta.yml");
}
sub download_recipe_local_tar_gz {
my ($url) = @_;
$Rex::Logger::silent = 0;
system "tar", "xzf", $url;
my $path = basename($url);
$path =~ s/\.tar\.gz$//;
chdir $path;
resolve_deps("meta.yml");
}
# upload rexfile to rex-jobcontrol (the complete directory)
sub upload_rexfile {
my (%option) = @_;
my $tmp_dir = File::Spec->tmpdir;
my $tmp_file = File::Spec->catfile( $tmp_dir, basename(getcwd) . ".tar.gz" );
my $login_url = $option{server} . "/login";
my $upload_url =
$option{server}
. "/project/"
. Rex::Helper::URI::encode( $option{project} )
. "/rexfile/new";
Rex::Logger::info("Creating tar.gz file out of this directory.");
my $dir = basename( getcwd() );
system "cd .. ; tar czf $tmp_file $dir";
# upload the file
my $ua = LWP::UserAgent->new( cookie_jar => {} );
#my $request = HTTP::Request->new(POST 'http://example.com', Content_Type => 'multipart/form-data', Content => [file_0 => ['options2.txt']]);
# first login
my $res = $ua->post( $login_url,
{ username => $option{username}, password => $option{password} } );
if ( $res->code != 302 ) {
print "Server not found or authentication wrong.\n";
exit 1;
}
my $up_request = POST(
$upload_url,
Content_Type => 'form-data',
Content => [
rexfile_archive => [$tmp_file],
rexfile_name => $option{name},
rexfile_description => $option{description}
]
);
my $up_res = $ua->request($up_request);
if ( $up_res->code != 302 ) {
print "Upload of Rexfile failed.\n";
exit 1;
}
unlink $tmp_file;
}
# execute job on Job-Control server
sub dispatch_execute_job {
my (%option) = @_;
my $login_url = $option{server} . "/login";
my $execute_url =
$option{server}
. "/project/"
. Rex::Helper::URI::encode( $option{project} ) . "/job/"
. Rex::Helper::URI::encode( $option{job} )
. "/execute";
# execute a job
my $ua = LWP::UserAgent->new( cookie_jar => {} );
# first login
my $res = $ua->post( $login_url,
{ username => $option{username}, password => $option{password} } );
if ( $res->code != 302 ) {
print "Server not found or authentication wrong.\n";
exit 1;
}
# then send the execute command
my $ex_request = POST(
$execute_url,
Content => [
sel_server => [ split( /[ ,]/, $option{hosts} ) ],
]
);
my $ex_res = $ua->request($ex_request);
if ( $ex_res->code != 302 ) {
print "Execute of job failed.\n";
exit 1;
}
}
sub download_recipe {
my ($name) = @_;
if ( $name =~ m/^(ssh|https|git):\/\// ) {
# seems to be a git link
download_recipe_git($name);
return;
}
if ( !-f "Rexfile" ) {
print STDERR "This is not a Rex project directory. There is no Rexfile.\n";
exit 1;
}
if ( !-d "lib" ) { mkdir "lib"; }
print "Getting dependencies for $name...\n";
my $deps = decode_json( get( sprintf( $DEPEND_SERVER, $name ) ) );
if ( scalar( @{$deps} ) > 0 ) {
print " Found: \n - " . join( "\n - ", @{$deps} ) . "\n";
for my $dep ( @{$deps} ) {
download_recipe($dep);
}
}
else {
print " None found.\n";
}
if ( !exists $opts->{"no-install-perl-deps"} ) {
print "Getting perl dependencies for $name...\n";
my $perl_deps = decode_json( get( sprintf( $PERL_DEPEND_SERVER, $name ) ) );
if ( scalar( @{$perl_deps} ) > 0 ) {
print " Found: \n - " . join( "\n - ", @{$perl_deps} ) . "\n";
for my $dep ( @{$perl_deps} ) {
install_perl_module($dep);
}
}
else {
print " None found.\n";
}
}
print "Downloading $name... ";
$name =~ s/::/\//g;
my $content = get( sprintf( $RECIPE_SERVER, $name ) );
open( my $fh, ">", "tmp-mod.tar.gz" ) or die($!);
binmode $fh;
print $fh $content;
close($fh);
chdir("lib");
system "tar", "xzf", "../tmp-mod.tar.gz";
chdir("..");
unlink("tmp-mod.tar.gz");
print "done.\n";
}
sub resolve_deps {
my ($file) = @_;
$Rex::Logger::silent = 0;
$file ||= "meta.yml";
if ( !-f $file ) {
return;
}
my $ref = YAML::LoadFile($file);
my ( %deps, %perl_deps );
if ( exists $ref->{Require} ) {
if ( ref $ref->{Require} eq "ARRAY" ) {
do { ref $_ eq "HASH" ? $deps{ $_->{name} } = $_ : $deps{$_} = $_ }
for @{ $ref->{Require} };
}
else {
%deps = %{ $ref->{Require} };
}
}
if ( exists $ref->{PerlRequire} ) {
if ( ref $ref->{PerlRequire} eq "ARRAY" ) {
do {
ref $_ eq "HASH" ? $perl_deps{ $_->{name} } = $_ : $perl_deps{$_} = $_;
}
for @{ $ref->{PerlRequire} };
}
else {
%perl_deps = %{ $ref->{PerlRequire} };
}
}
Rex::Logger::debug("Found dependencies: ");
Rex::Logger::debug( Dumper( \%deps ) );
for my $req ( keys %deps ) {
if ( ref $deps{$req} ) {
if ( exists $deps{$req}->{git} ) {
# git dep
my $branch = "master";
if ( exists $deps{$req}->{branch} ) {
$branch = $deps{$req}->{branch};
}
my @path_parts = split /::/, $req;
my $path =
File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
"lib", @path_parts );
my $parent_path = dirname $path;
if ( !-d $parent_path ) {
mkdir $parent_path;
}
if ( -d $path && !-d "$path/.git" ) {
Rex::Logger::info( "$req not under git control. Skipping.", "warn" );
next;
}
if ( -d "$path/.git" ) {
rmdir $path;
}
Rex::Logger::info("Cloning $deps{$req}->{git}#$branch to $path");
system "git", "clone", $deps{$req}->{git}, "-b", $branch, $path;
resolve_deps("$path/meta.yml");
}
}
else {
download_recipe($req);
}
}
Rex::Logger::debug("Found perl dependencies: ");
Rex::Logger::debug( Dumper( \%perl_deps ) );
for my $req ( keys %perl_deps ) {
if ( ref $perl_deps{$req} ) {
if ( exists $perl_deps{$req}->{git} ) {
# git dep
my $branch = "master";
if ( exists $perl_deps{$req}->{branch} ) {
$branch = $perl_deps{$req}->{branch};
}
my $curdir = getcwd;
my $path = File::Spec->catdir( File::Spec->tmpdir, "tmp-build-$$" );
my $lib_path =
File::Spec->catdir( File::Spec->rel2abs( File::Spec->curdir() ),
"lib", "perl" );
my $parent_path = dirname $lib_path;
if ( !-d $parent_path ) {
mkdir $parent_path;
}
Rex::Logger::info("Cloning $perl_deps{$req}->{git}#$branch to $path");
system "git", "clone", $perl_deps{$req}->{git}, "-b", $branch, $path;
chdir $path;
system "cpanm", "-l", $lib_path, ".";
chdir $curdir;
rmdir $path;
}
}
else {
# we need relative directories, because auf a cpanm bug on windows.
my $lib_path = File::Spec->catdir( File::Spec->curdir(), "lib", "perl" );
eval "use $req";
if ($@) {
system "cpanm", "-l", $lib_path, $req;
}
}
}
}
sub install_perl_module {
my ($mod) = @_;
print "Checking $mod: ";
eval "use $mod";
if ($@) {
print "[failed]\n";
}
else {
print "[ok]\n";
return;
}
print "Trying to install $mod... ";
my $cmd = "cpanm";
my $out = qx($cmd --help 2>&1);
if ( $? != 0 ) {
$cmd = "cpan";
$out = qx($cmd -h 2>&1);
if ( $? != 0 ) {
print "[failed]\n";
print "Can't find cpanm or cpan. Please install $mod manually.\n";
return;
}
}
my $cpanm_opts = "";
if ( exists $opts->{sudo} ) {
$cmd = "sudo $cmd";
}
$out = qx($cmd $cpanm_opts $mod 2>&1);
open( my $log, ">>", "rexify-install.log" ) or die($!);
print $log $out;
close($log);
if ( $? != 0 ) {
print "[failed]\n";
print
"!! Please install $mod manually. See rexify-install.log for more details.\n";
}
else {
print "[ok]\n";
}
}
if ( exists $opts->{bundle} ) {
# bundle everything to an application
CORE::mkdir("rex.payload");
system "cp -vR * rex.payload/ 2>/dev/null";
system "rm -rf rex.payload/rex.payload";
my $rex = File::Spec->catfile( dirname($0), "rex" );
my $tmp_args_file =
File::Spec->catfile( File::Spec->tmpdir(), "pp.args.$$.tmp" );
open( my $fh, ">", $tmp_args_file ) or die($!);
print $fh template('@pp.args.tpl');
close($fh);
# build the perl and application
if ( -d '/usr/lib/rex/lib' ) {
system
"pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex";
}
else {
system
"pp -I /usr/lib/rex/lib \@$tmp_args_file -o rex.payload/rex.bin $rex";
}
# find libssh2.so and libexpat.so
my $arch = qx{uname -m};
chomp $arch;
my $elf = "ELF32";
if ( $arch eq "x86_64" ) {
$elf = "ELF64";
}
CORE::mkdir( File::Spec->catdir( "rex.payload", "lib.bin" ) );
my @libexpat =
qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libexpat.so*' -type f 2>/dev/null};
my @libssh2 =
qx{find /usr/lib /usr/lib32 /usr/lib64 /lib -name 'libssh2.so*' -type f 2>/dev/null};
chomp @libexpat;
chomp @libssh2;
for my $f ( @libexpat, @libssh2 ) {
my $elf_class = qx{readelf -h $f | grep Class:};
# only copy architecture specific files
# currently no multi arch support
if ( $elf_class =~ m/$elf/ ) {
system "cp -va $f " . File::Spec->catdir( "rex.payload", "lib.bin" );
}
}
system "cd rex.payload/lib.bin ; ln -s "
. basename( $libssh2[0] )
. " libssh2.so.1";
system "cd rex.payload/lib.bin ; ln -s "
. basename( $libexpat[0] )
. " libexpat.so.1";
# create wrapper script.
open( my $fh2, ">", "rex.sh" ) or die($!);
print $fh2 template( '@bundle.sh.tpl', task => 'setup' );
close($fh2);
system "cd rex.payload ; tar czf ../rex.payload.tar.gz *";
system
"cat rex.sh rex.payload.tar.gz >rex.selfextract.sbx ; chmod 755 rex.selfextract.sbx";
CORE::unlink($tmp_args_file);
CORE::unlink("rex.sh");
system "rm -rf rex.payload";
CORE::unlink("rex.payload.tar.gz");
exit 0;
}
if ( exists $opts->{upload}
&& exists $opts->{server}
&& exists $opts->{username}
&& exists $opts->{password}
&& exists $opts->{project}
&& exists $opts->{name} )
{
$opts->{description} ||= "";
upload_rexfile( %{$opts} );
exit 0;
}
if ( exists $opts->{execute}
&& exists $opts->{server}
&& exists $opts->{username}
&& exists $opts->{password}
&& exists $opts->{project}
&& exists $opts->{hosts}
&& exists $opts->{job} )
{
dispatch_execute_job( %{$opts} );
exit 0;
}
if ( exists $opts->{search} ) {
my $search_string = $opts->{search};
# only a search
print "Downloading recipes.yml ... ";
my $recipes = get($SEARCH_SERVER);
print " done.\n";
print "Searching...\n\n";
my $data = Load($recipes);
for my $mod ( keys %{$data} ) {
if ( $mod =~ qr{$search_string}i ) {
print_found( $mod, $data->{$mod} );
next;
}
if ( $data->{$mod}->{Description} =~ qr{$search_string}i ) {
print_found( $mod, $data->{$mod} );
}
}
exit 0;
}
if ( exists $opts->{"update-from-git"} ) {
update_from_git();
exit 0;
}
if ( exists $opts->{init} ) {
if ( -f $opts->{init} ) {
download_recipe_local_tar_gz( $opts->{init} );
}
else {
download_recipe_git( $opts->{init} );
}
exit 0;
}
if ( exists $opts->{use} && $ARGV[0] =~ /^\-\-use/ ) {
if ( $opts->{use} ) {
if ( !ref $opts->{use} ) {
$opts->{use} = [ $opts->{use} ];
}
for my $use_mod ( @{ $opts->{use} } ) {
download_recipe($use_mod);
}
}
exit 0;
}
if ( exists $opts->{"resolve-deps"} && $opts->{"resolve-deps"} ) {
resolve_deps("meta.yml");
exit 0;
}
if ( exists $opts->{"create-module"} ) {
my $dir = $ARGV[0];
my $module_name = $dir;
if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) {
print "USAGE: $0 Module::Name --create-module\n";
print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n";
exit 1;
}
if ( -f "Rexfile" && $opts->{"create-module"} ) {
$dir = "lib/$dir";
}
if ( $dir =~ m/\-\-create\-module/ ) {
print "USAGE: $0 Module::Name --create-module\n";
print " Allowed characters: a-z, A-Z, _, 0-9, '::'.\n";
exit 1;
}
$dir =~ s/::/\//g;
print "Creating module $module_name...\n";
print " mkdir $dir\n";
mkdir $dir, recursive => 1;
chdir $dir;
print " Creating template file: __module__.pm\n";
file "__module__.pm",
content =>
template( '@module.pm.tpl', dir => $dir, module_name => $module_name );
print " Creating template file: meta.yml\n";
file "meta.yml",
content =>
template( '@meta.yml.tpl', dir => $dir, module_name => $module_name );
print "\n";
print "Your module has been created in $dir.\n";
exit 0;
}
my $dir = $ARGV[0];
if ( defined $ARGV[1] && $ARGV[1] !~ m/^\-\-/ ) {
$dir = $ARGV[1];
}
if ( $dir !~ m/^[a-zA-Z][a-zA-Z_:0-9]+$/ ) {
print "USAGE: $0 Project::Name\n";
print " Allowed characters: a-z, A-Z, 0-9, _, '::'.\n";
exit 1;
}
if ( exists $opts->{template}
&& -f $opts->{template}
&& $opts->{template} !~ m/^\// )
{
my $cwd = getcwd;
$opts->{template} = "$cwd/" . $opts->{template};
}
elsif ( exists $opts->{template} && $opts->{template} !~ m/^\// ) {
$opts->{template} = '@' . $opts->{template};
}
unless ( -d $dir ) {
print "Created $dir\n";
mkdir($dir);
}
print "chdir to $dir\n";
chdir($dir);
unless ( -d 'lib' ) {
mkdir('lib');
}
unless ( -f 'lib' . $ARGV[0] . '.pm' ) {
open( my $fh, ">", "lib/$ARGV[0].pm" ) or die($!);
print $fh template( '@libfile', lib => $ARGV[0] );
close($fh);
print STDERR "Created lib/Rex/$ARGV[0].pm\n";
if ( $opts->{template} ) {
open( $fh, ">", "Rexfile" ) or die($!);
print $fh template( $opts->{template}, lib => $ARGV[0] );
close($fh);
}
else {
open( $fh, ">", "Rexfile" ) or die($!);
print $fh template( '@rexfile', lib => $ARGV[0] );
close($fh);
}
if ( $opts->{use} ) {
if ( !ref $opts->{use} ) {
$opts->{use} = [ $opts->{use} ];
}
for my $use_mod ( @{ $opts->{use} } ) {
download_recipe($use_mod);
}
}
print STDERR "Created Rexfile.\n";
print STDERR "Done.\n\nNow edit Rexfile to suit your needs.\n";
print STDERR "You can edit $dir/lib/$ARGV[0].pm to define tasks.\n";
print STDERR
"\nIf you need assistance, get in touch via one of our support channels!\n";
}
else {
if ( $opts->{use} ) {
if ( !ref $opts->{use} ) {
$opts->{use} = [ $opts->{use} ];
}
for my $use_mod ( @{ $opts->{use} } ) {
download_recipe($use_mod);
}
}
exit;
}
sub get {
my ($url) = @_;
my $ua = LWP::UserAgent->new;
$ua->env_proxy;
if ( $AUTH_USER && $AUTH_PASSWORD ) {
my ($netloc) = ( $RECIPE_SERVER =~ m/^https?:\/\/([^\/]+)\// );
unless ( $netloc =~ m/\:\d+$/ ) {
if ( $netloc =~ m/^https/ ) {
$netloc .= ":443";
}
else {
$netloc .= ":80";
}
}
$ua->credentials( $netloc, $AUTH_REALM, $AUTH_USER, $AUTH_PASSWORD );
}
my $resp = $ua->get($url);
if ( $resp->is_success ) {
return $resp->decoded_content;
}
}
__DATA__
@rexfile
# enable new Features
use Rex -feature => 0.40;
# set your username
set user => "<user>";
# set your password
set password => "<password>";
# enable password authentication
set -passauth;
# put your server in this group
set group => "servers" => "server1", "server2";
# now load every module via ,,require''
require <%= $::lib %>;
@end
@libfile
package <%= $::lib %>;
use Rex -base;
desc "Get uptime of server";
task "uptime", group => 'servers', sub {
say run "uptime";
};
1;
@end
@box
use strict;
use warnings;
use Rex -feature => 0.40;
use Rex::Commands::Box;
set user => '<user>';
set password => '<password>';
set -passauth;
#
# CALL:
# rex init --name=<%= $::lib %> --url=http://box.rexify.org/box/ubuntu-server-12.10-amd64.ova
desc "Initialize and start the VM: rex init --name=vmname [--url=http://...]";
task "init", sub {
my $param = shift;
box {
my ($box) = @_;
$box->name($param->{name});
# where to download the base image
$box->url($param->{url});
# default is nat
#$box->network(1 => {
# type => "bridged",
# bridge => "eth0",
#});
# only works with network type = nat
# if a key ssh is present, rex will use this to log into the vm
# you need this if you run VirtualBox not on your local host.
$box->forward_port(ssh => [2222 => 22]);
# share a folder from the host system
#$box->share_folder("sharename" => "/path/on/host/system");
# define the authentication to the box
# if you're downloading one from box.rexify.org this is the default.
$box->auth(
user => "root",
password => "box",
);
# if you want to provision the machine,
# you can define the tasks to do that
$box->setup(qw/install_webserver/);
};
};
#
# CALL:
# rex down --name=<%= $::lib %>
desc "Stop the VM: rex down --name=vmname";
task "down", sub {
my $param = shift;
my $box = Rex::Commands::Box->new(name => $param->{name});
$box->stop;
};
task "install_webserver", sub {
# update package db
update_package_db;
# install packages / customize the vm
install "apache2";
};
require <%= $::lib %>;
@end
@module.pm.tpl
package <%= $::module_name %>;
use Rex -base;
task example => sub {
my $output = run "uptime";
say $output;
};
1;
=pod
=head1 NAME
$::module_name - {{ SHORT DESCRIPTION }}
=head1 DESCRIPTION
{{ LONG DESCRIPTION }}
=head1 USAGE
{{ USAGE DESCRIPTION }}
include qw/<%= $::module_name %>/;
task yourtask => sub {
<%= $::module_name %>::example();
};
=head1 TASKS
=over 4
=item example
This is an example Task. This task just output's the uptime of the system.
=back
=cut
@end
@meta.yml.tpl
Name: <%= $::module_name %>
Description: {{ DESCRIPTION }}
Author: {{ your name <your.name@email.com> }}
License: {{ THE LICENSE }}
# Only if you have dependencies to other Rex Modules.
Require:
- Other::Rex::Module
- 2nd::Rex::Module
@end
@pp.args.tpl
-M Net::OpenSSH::ShellQuoter::POSIX
-M Net::SFTP::Foreign::Backend::Unix
-M Sys::Syslog
-M Net::OpenSSH
-M Net::SFTP::Foreign
-M UNIVERSAL
-M Rex::Virtualization
-M Rex::User::Linux
-M Rex::User::FreeBSD
-M Rex::User::SunOS
-M Rex::User::OpenWrt
-M Rex::User::NetBSD
-M Rex::User::OpenBSD
-M Rex::Virtualization::VBox::delete
-M Rex::Virtualization::VBox::list
-M Rex::Virtualization::VBox::info
-M Rex::Virtualization::VBox::shutdown
-M Rex::Virtualization::VBox::reboot
-M Rex::Virtualization::VBox::status
-M Rex::Virtualization::VBox::import
-M Rex::Virtualization::VBox::forward_port
-M Rex::Virtualization::VBox::create
-M Rex::Virtualization::VBox::bridge
-M Rex::Virtualization::VBox::option
-M Rex::Virtualization::VBox::start
-M Rex::Virtualization::VBox::share_folder
-M Rex::Virtualization::VBox::destroy
-M Rex::Virtualization::VBox::guestinfo
-M Rex::Virtualization::VBox
-M Rex::Virtualization::LibVirt
-M Rex::Virtualization::Docker
-M Rex::Virtualization::LibVirt::delete
-M Rex::Virtualization::LibVirt::blklist
-M Rex::Virtualization::LibVirt::list
-M Rex::Virtualization::LibVirt::info
-M Rex::Virtualization::LibVirt::shutdown
-M Rex::Virtualization::LibVirt::reboot
-M Rex::Virtualization::LibVirt::dumpxml
-M Rex::Virtualization::LibVirt::status
-M Rex::Virtualization::LibVirt::import
-M Rex::Virtualization::LibVirt::create
-M Rex::Virtualization::LibVirt::option
-M Rex::Virtualization::LibVirt::start
-M Rex::Virtualization::LibVirt::vncdisplay
-M Rex::Virtualization::LibVirt::clone
-M Rex::Virtualization::LibVirt::iflist
-M Rex::Virtualization::LibVirt::destroy
-M Rex::Virtualization::LibVirt::hypervisor
-M Rex::Virtualization::LibVirt::guestinfo
-M Rex::Virtualization::Base
-M Rex::Virtualization::Docker::delete
-M Rex::Virtualization::Docker::list
-M Rex::Virtualization::Docker::daemon
-M Rex::Virtualization::Docker::info
-M Rex::Virtualization::Docker::shutdown
-M Rex::Virtualization::Docker::reboot
-M Rex::Virtualization::Docker::create
-M Rex::Virtualization::Docker::start
-M Rex::Virtualization::Docker::destroy
-M Rex::Shared::Var::Scalar
-M Rex::Shared::Var::Array
-M Rex::Shared::Var::Hash
-M Rex::Shared::Var
-M Rex::Hardware
-M Rex::Args::Single
-M Rex::Args::Integer
-M Rex::Args::String
-M Rex::Inventory::DMIDecode
-M Rex::Inventory::Proc::Cpuinfo
-M Rex::Inventory::SMBios::SystemInformation
-M Rex::Inventory::SMBios::CPU
-M Rex::Inventory::SMBios::Memory
-M Rex::Inventory::SMBios::BaseBoard
-M Rex::Inventory::SMBios::Section
-M Rex::Inventory::SMBios::MemoryArray
-M Rex::Inventory::SMBios::Bios
-M Rex::Inventory::Hal
-M Rex::Inventory::SMBios
-M Rex::Inventory::Bios
-M Rex::Inventory::HP::ACU
-M Rex::Inventory::Hal::Object::Net
-M Rex::Inventory::Hal::Object::Storage
-M Rex::Inventory::Hal::Object::Volume
-M Rex::Inventory::Hal::Object
-M Rex::Inventory::DMIDecode::SystemInformation
-M Rex::Inventory::DMIDecode::CPU
-M Rex::Inventory::DMIDecode::Memory
-M Rex::Inventory::DMIDecode::BaseBoard
-M Rex::Inventory::DMIDecode::Section
-M Rex::Inventory::DMIDecode::MemoryArray
-M Rex::Inventory::DMIDecode::Bios
-M Rex::Inventory::Proc
-M Rex::File::Parser::Data
-M Rex::File::Parser::Ini
-M Rex::Notify
-M Rex::Logger
-M Rex::Commands
-M Rex::Hook
-M Rex::Exporter
-M Rex::Box::VBox
-M Rex::Box::Amazon
-M Rex::Box::Base
-M Rex::Box::KVM
-M Rex::Output::JUnit
-M Rex::Output::Base
-M Rex::Report::YAML
-M Rex::Report::Base
-M Rex::Inventory
-M Rex::FS::File
-M Rex::Output
-M Rex::Cron
-M Rex::Batch
-M Rex::CMDB
-M Rex::TaskList
-M Rex::Pkg::SuSE
-M Rex::Pkg::Debian
-M Rex::Pkg::Ubuntu
-M Rex::Pkg::Gentoo
-M Rex::Pkg::FreeBSD
-M Rex::Pkg::SunOS
-M Rex::Pkg::OpenWrt
-M Rex::Pkg::Redhat
-M Rex::Pkg::NetBSD
-M Rex::Pkg::Base
-M Rex::Pkg::ALT
-M Rex::Pkg::Mageia
-M Rex::Pkg::OpenBSD
-M Rex::Pkg::SunOS::pkg
-M Rex::Pkg::SunOS::OpenCSW
-M Rex::Template
-M Rex::Profiler
-M Rex::User
-M Rex::Box
-M Rex::Resource::Common
-M Rex::Cloud
-M Rex::SCM::Subversion
-M Rex::SCM::Git
-M Rex::Report
-M Rex::Value
-M Rex::Group
-M Rex::CMDB::YAML
-M Rex::CMDB::Base
-M Rex::Group::Entry::Server
-M Rex::Group::Lookup::Command
-M Rex::Group::Lookup::File
-M Rex::Group::Lookup::DBI
-M Rex::Group::Lookup::YAML
-M Rex::Group::Lookup::XML
-M Rex::Group::Lookup::INI
-M Rex::Task
-M Rex::Resource
-M Rex::Interface::Fs::Sudo
-M Rex::Interface::Fs::HTTP
-M Rex::Interface::Fs::Local
-M Rex::Interface::Fs::Base
-M Rex::Interface::Fs::SSH
-M Rex::Interface::Fs::OpenSSH
-M Rex::Interface::File::Sudo
-M Rex::Interface::File::HTTP
-M Rex::Interface::File::Local
-M Rex::Interface::File::Base
-M Rex::Interface::File::SSH
-M Rex::Interface::File::OpenSSH
-M Rex::Interface::Cache::YAML
-M Rex::Interface::Cache::Base
-M Rex::Interface::Connection::Fake
-M Rex::Interface::Connection::HTTP
-M Rex::Interface::Connection::Local
-M Rex::Interface::Connection::HTTPS
-M Rex::Interface::Connection::Base
-M Rex::Interface::Connection::SSH
-M Rex::Interface::Connection::OpenSSH
-M Rex::Interface::File
-M Rex::Interface::Connection
-M Rex::Interface::Executor
-M Rex::Interface::Shell
-M Rex::Interface::Exec::Sudo
-M Rex::Interface::Exec::HTTP
-M Rex::Interface::Exec::Local
-M Rex::Interface::Exec::Base
-M Rex::Interface::Exec::SSH
-M Rex::Interface::Exec::OpenSSH
-M Rex::Interface::Executor::Default
-M Rex::Interface::Executor::Base
-M Rex::Interface::Exec
-M Rex::Interface::Fs
-M Rex::Interface::Shell::Ksh
-M Rex::Interface::Shell::Zsh
-M Rex::Interface::Shell::Ash
-M Rex::Interface::Shell::Default
-M Rex::Interface::Shell::Sh
-M Rex::Interface::Shell::Bash
-M Rex::Interface::Shell::Tcsh
-M Rex::Interface::Shell::Base
-M Rex::Interface::Shell::Csh
-M Rex::Interface::Cache
-M Rex::Fork::Task
-M Rex::Fork::Manager
-M Rex::CLI
-M Rex::Test::Base
-M Rex::Test::Base::has_content
-M Rex::Test::Base::has_file
-M Rex::Test::Base::has_package
-M Rex::Test::Base::has_service_stopped
-M Rex::Test::Base::has_service_running
-M Rex::Constants
-M Rex::Hardware::Memory
-M Rex::Hardware::Network
-M Rex::Hardware::VirtInfo
-M Rex::Hardware::Swap
-M Rex::Hardware::Kernel
-M Rex::Hardware::Host
-M Rex::Hardware::Network::Linux
-M Rex::Hardware::Network::FreeBSD
-M Rex::Hardware::Network::Solaris
-M Rex::Hardware::Network::NetBSD
-M Rex::Hardware::Network::OpenBSD
-M Rex::Hardware::Network::Darwin
-M Rex::Cloud::Amazon
-M Rex::Cloud::Base
-M Rex::Cloud::Jiffybox
-M Rex::Cloud::OpenStack
-M Rex::Transaction
-M Rex::Commands::LVM
-M Rex::Commands::Virtualization
-M Rex::Commands::Sysctl
-M Rex::Commands::SimpleCheck
-M Rex::Commands::Rsync
-M Rex::Commands::Notify
-M Rex::Commands::Tail
-M Rex::Commands::Inventory
-M Rex::Commands::Cron
-M Rex::Commands::DB
-M Rex::Commands::File
-M Rex::Commands::Gather
-M Rex::Commands::Network
-M Rex::Commands::User
-M Rex::Commands::Box
-M Rex::Commands::Upload
-M Rex::Commands::JobControl
-M Rex::Commands::Cloud
-M Rex::Commands::Run
-M Rex::Commands::Download
-M Rex::Commands::Process
-M Rex::Commands::Sync
-M Rex::Commands::Kernel
-M Rex::Commands::MD5
-M Rex::Commands::Iptables
-M Rex::Commands::Partition
-M Rex::Commands::Service
-M Rex::Commands::Pkg
-M Rex::Commands::Fs
-M Rex::Commands::Host
-M Rex::Commands::SCM
-M Rex::Commands::Mkfs
-M Rex::Service
-M Rex::Pkg
-M Rex::Cron::Linux
-M Rex::Cron::SunOS
-M Rex::Cron::Base
-M Rex::Config
-M Rex::Helper::System
-M Rex::Helper::Run
-M Rex::Helper::SSH2
-M Rex::Helper::DBI
-M Rex::Helper::Encode
-M Rex::Helper::Array
-M Rex::Helper::INI
-M Rex::Helper::Misc
-M Rex::Helper::SSH2::Expect
-M Rex::Helper::UserAgent
-M Rex::Helper::Hash
-M Rex::Helper::URI
-M Rex::Helper::Path
-M Rex::TaskList::Parallel_ForkManager
-M Rex::TaskList::Base
-M Rex::Require
-M Rex::Args
-M Rex::Sudo::File
-M Rex::Test
-M Rex::Service::SuSE
-M Rex::Service::Debian
-M Rex::Service::ALT::systemd
-M Rex::Service::Gentoo::systemd
-M Rex::Service::Mageia::systemd
-M Rex::Service::Ubuntu
-M Rex::Service::Gentoo
-M Rex::Service::FreeBSD
-M Rex::Service::SunOS
-M Rex::Service::Redhat::systemd
-M Rex::Service::OpenWrt
-M Rex::Service::Redhat
-M Rex::Service::NetBSD
-M Rex::Service::Base
-M Rex::Service::ALT
-M Rex::Service::Mageia
-M Rex::Service::OpenBSD
-M Rex::Service::SunOS::svcadm
-M Rex::Service::SuSE::systemd
-M Rex::Service::Arch::systemd
-M Rex
-M Net::SSH2
@end
@bundle.sh.tpl
#!/bin/bash
echo ""
echo "Self Extracting Rex Installer"
echo ""
export TMPDIR=`mktemp -d /tmp/rex.bundle.XXXXXX`
ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`
echo -n "Extracting archive... "
echo
tail -n+$ARCHIVE $0 | tar xz -C $TMPDIR
if [ "$?" != "0" ]; then
echo "failed."
exit 1
fi
echo "done."
CDIR=`pwd`
cd $TMPDIR
export LD_LIBRARY_PATH=`pwd`/lib.bin:$LD_LIBRARY_PATH
./rex.bin -f `pwd`/Rexfile $*
cd $CDIR
rm -rf $TMPDIR
exit 0
__ARCHIVE_BELOW__
@end