Respite
view release on metacpan or search on metacpan
lib/Respite/CommandLine.pm view on Meta::CPAN
my ($self, $preload) = @_;
return $self->{'dispatch_factory'} ||= do {
my $meta = $self->api_meta || $self->dispatch_class || throw "Missing one of api_meta or dispatch_class";
if (!ref $meta) {
(my $file = "$meta.pm") =~ s|::|/|g;
throw "Failed to load dispatch class", {class => $meta, file => $file, msg => $@} if !$meta->can('new') && !eval { require $file };
throw "Specified class does not have a run_method method", {class => $meta} if ! $meta->can('run_method');
sub { $meta->new(@_) };
} elsif ($meta->{'remote'}) {
require Respite::Client;
sub { Respite::Client->new({%{shift() || {}}, %$meta}) };
} else {
require Respite::Base;
Respite::Base->new({api_meta => $meta})->api_preload if $preload;
sub { Respite::Base->new({%{shift() || {}}, api_meta => $meta}) };
}
};
}
###----------------------------------------------------------------###
sub run_commandline { shift->run(@_) }
sub run {
my ($self, $args) = @_;
$self = $self->new($args) if ! ref($self);
my $obj = $self->dispatch_factory->();
my $ARGV = $args->{'argv'} || $self->{'argv'} || \@ARGV;
my $method = shift(@$ARGV) || return print $self->_pod($obj, {brief => 1});
return print $self->_pod($obj, {format => $1}) if $method =~ /^-{0,2}(help|h|pod|p)$/;
return print $self->_pod($obj, {method => $method, format => $_}) for grep {/^-{1,2}(help|h|pod|p)$/} @ARGV;
throw "Odd number of args passed to commandline. If you want the last value to be undef pass a :null", {argv => $ARGV, _pretty=>1} if @$ARGV % 2;
my $req = {@$ARGV};
throw "Cannot use '' as a keyname - possible invalid args", {argv => $ARGV} if exists $req->{''};
foreach my $key (keys %$req) { $req->{$key} = __PACKAGE__->can("_$1")->() if $req->{$key} && $req->{$key} =~ /^:(null|true|false)$/ }
$req = Data::URIEncode::flat_to_complex($req) || {} if !$self->{'no_data_uriencode'} && eval { require Data::URIEncode };
my $data = $self->_run_method($obj,$method, $req);
my $meta = $ENV{'SHOW_META'} ? $self->_run_method($obj,"${method}__meta", $req) : undef;
$self->print_data($data, $req, $meta);
exit(1) if ref($data) && $data->{'error'};
}
sub run_method {
my ($self, $method, $args) = @_;
$self = $self->new($args) if ! ref($self);
my $obj = $self->dispatch_factory->();
return $self->_run_method($obj, $method, $args);
}
sub _run_method {
my ($self, $obj, $method, $args, $extra) = @_;
local $args->{'_c'} = ['commandline'] if $obj->can('config') ? !$obj->config(no_trace => undef) : 1;
local $obj->{'remote_ip'} = local $obj->{'api_ip'} = ($ENV{'REALUSER'} || $ENV{'SUDO_USER'}) ? 'sudo' : 'cmdline';
local $obj->{'api_brand'} = $ENV{'BRAND'} || $ENV{'PROV'} if $obj->isa('Respite::Base') && ($ENV{'BRAND'} || $ENV{'PROV'});
local $obj->{'remote_user'} = $ENV{'REALUSER'} || $ENV{'SUDO_USER'} || $ENV{'REMOTE_USER'} || $ENV{'USER'} || (getpwuid($<))[0] || '-unknown-';
local $obj->{'token'} = $self->{'token'} || $ENV{'ADMIN_Respite_TOKEN'} if $self->{'token'} || $ENV{'ADMIN_Respite_TOKEN'};
local $obj->{'transport'} = 'cmdline';
$obj->commandline_init($method, $args, $self) if $obj->can('commandline_init');
my $run = sub {
my $data = eval { $obj->can('run_method') ? $obj->run_method(@_) : $obj->$method($args, ($extra ? $extra : ())) };
$data = $@ if ! ref $data;
return !ref($data) ? {error => 'Commandline failed', msg => $data} : (blessed($data) && $data->can('data')) ? $data->data : $data;
};
my $ref = $run->($method, $args, $extra);
while ($ref->{'error'}) {
last if !$ref->{'type'} || $ref->{'type'} !~ /^token_\w+$/;
last if $self->{'no_token_retry'};
warn "Prompting for authorization and retry ($ref->{'type'}: $ref->{'error'})\n";
eval { require IO::Prompt } || throw "Please install IO::Prompt to authenticate from commandline", {msg => $@};
my $user = ''.IO::Prompt::prompt(" Web Auth Username: ", -d => $obj->{'remote_user'}) || $obj->{'remote_user'};
my $pass = ''.IO::Prompt::prompt(" Web Auth Password ($user): ", -e => '*') || throw "Cannot proceed without password";
my $key = !$obj->can('config') ? $config::config{'plaintext_public_key'}
: $obj->config(plaintext_public_key => sub { $obj->config(plaintext_public_key => sub { $obj->_configs->{'plaintext_public_key'} }, 'emp_auth') });
if (!$key) {
warn " Could not find plaintext_public_key in config - sending plaintext password\n";
} elsif (!eval { require Crypt::OpenSSL::RSA }) {
warn " (Crypt::OpenSSL::RSA is not installed - install to avoid sending plaintext password)\n";
} else {
my $c = Crypt::OpenSSL::RSA->new_public_key($key);
my $len = length($pass) + 1;
$pass = pack 'u*', $c->encrypt(pack "Z$len", $pass);
$pass = "RSA".length($pass).":$pass";
}
$obj->{'token'} = "$user/i:cmdline/$pass";
$ref = $run->(hello => {test_auth => 1});
$self->{'token'} = $obj->{'token'} = $ref->{'token'} || throw "Did not get a token back from successful test_auth", {data => $ref};
warn "\nexport ADMIN_Respite_TOKEN=$obj->{'token'}\n\n";
$ref = $run->($method, $args, $extra);
}
return $ref;
}
sub print_data {
my ($self, $data, $args, $meta) = @_;
if ($ENV{'CSV'} and my @fields = grep {ref($data->{$_}) eq 'ARRAY' && ref($data->{$_}->[0]) eq 'HASH'} sort keys %$data) {
require Text::CSV_XS;
my $csv = Text::CSV_XS->new({eol => "\n"});
foreach my $field (@fields) {
print "----- $field -------------------------\n" if @fields > 1;
my @keys = sort {($a eq 'id') ? -1 : ($b eq 'id') ? 1 : $a cmp $b } keys %{ $data->{'rows'}->[0] };
$csv->print(\*STDOUT, \@keys);
$csv->print(\*STDOUT, [map {ref($_) eq 'ARRAY' ? join(",",@$_) : ref($_) eq 'HASH' ? join(",",%$_) : $_} @$_{@keys}]) for @{ $data->{'rows'} };
}
exit;
}
if ($ENV{'YAML'}) {
eval { require YAML } || throw "Could not load YAML for output", {msg => $@};
print YAML->new->Dump($data);
} elsif ($ENV{'JSON'} || ! eval { require Text::PrettyTable }) {
eval { require JSON } || throw "Could not load JSON for output", {msg => $@};
my $json = JSON->new->utf8->allow_nonref->convert_blessed->pretty->canonical;
print "meta = ".$json->encode($meta) if $ENV{'SHOW_META'};
print "args = ".$json->encode($args);
print "data = ".$json->encode($data);
( run in 2.145 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )