File Coverage

blib/lib/Clustericious/Client/Command.pm
Criterion Covered Total %
statement 64 148 43.2
branch 23 86 26.7
condition 16 72 22.2
subroutine 11 16 68.7
pod 1 1 100.0
total 115 323 35.6


line stmt bran cond sub pod time code
1             package Clustericious::Client::Command;
2              
3 2     2   25324 use strict;
  2         7  
  2         84  
4 2     2   13 use warnings;
  2         6  
  2         126  
5              
6             # ABSTRACT: Command line type processing for clients.
7             our $VERSION = '0.85'; # VERSION
8              
9              
10 2     2   14 use File::Basename qw/basename/;
  2         4  
  2         148  
11 2     2   15 use YAML::XS qw(Load Dump LoadFile);
  2         5  
  2         144  
12 2     2   13 use Log::Log4perl qw/:easy/;
  2         4  
  2         19  
13 2     2   1678 use Scalar::Util qw/blessed/;
  2         6  
  2         114  
14 2     2   2170 use Data::Rmap qw/rmap_ref/;
  2         2997  
  2         134  
15 2     2   14 use File::Temp;
  2         5  
  2         172  
16              
17 2     2   12 use Clustericious::Log;
  2         6  
  2         19  
18 2     2   164 use Clustericious::Client::Meta;
  2         3  
  2         4121  
19              
20             sub _usage {
21 0     0   0 my $class = shift;
22 0         0 my $client = shift;
23 0         0 my $msg = shift;
24 0         0 my $routes = Clustericious::Client::Meta->routes(ref $client);
25 0         0 my $objects = Clustericious::Client::Meta->objects(ref $client);
26 0 0       0 print STDERR $msg,"\n" if $msg;
27 0         0 print STDERR "Usage:\n";
28 0         0 my $name = basename($0);
29 0 0 0     0 print STDERR <
30 0         0 @{[ join "\n", map " $name [opts] $_->[0] $_->[1]", @$routes ]}
31             EOPRINT
32 0 0 0     0 print STDERR <
33 0         0 $name [opts]
34             $name [opts]
35             $name [opts] search [--key value]
36             $name [opts] create []
37             $name [opts] update []
38             $name [opts] delete
39              
40             where "opts" are as described in Log::Log4perl::CommandLine, or
41             may be "--remote " to specify a remote to use from the
42             config file (see Clustericious::Client).
43              
44             and "" may be one of the following :
45             @{[ join "\n", map " $_->[0] $_->[1]", @$objects ]}
46              
47             EOPRINT
48              
49 0         0 exit 0;
50             }
51              
52              
53             our $Ssh = "ssh -o StrictHostKeyChecking=no -o BatchMode=yes -o PasswordAuthentication=no";
54             sub _expand_remote_glob {
55             # Given a glob, e.g. omidev.gsfc.nasa.gov:/devsips/app/*/doc/Description.txt
56             # Return a list of filenames with the host prepended to each one, e.g.
57             # omidev.gsfc.nasa.gov:/devsips/app/foo-1/doc/Description.txt
58             # omidev.gsfc.nasa.gov:/devsips/app/bar-2/doc/Description.txt
59 0     0   0 my $pattern = shift;
60 0 0       0 return ( $pattern ) unless $pattern =~ /^(\S+):(.*)$/;
61 0         0 my ($host,$file) = ( $1, $2 );
62 0 0       0 return ( $pattern ) unless $file =~ /[*?]/;
63 0         0 INFO "Remote glob : $host:$file";
64 0         0 my $errs = File::Temp->new();
65 0         0 my @filenames = `$Ssh $host ls $file 2>$errs`;
66 0 0       0 LOGDIE "Error ssh $host ls $file returned (code $?)".`tail -2 $errs` if $?;
67 0         0 return map "$host:$_", @filenames;
68             }
69              
70             sub _load_yaml {
71             # _load_yaml can take a local filename or a remote ssh host + filename and
72             # returns parsed yaml content.
73 0     0   0 my $filename = shift;
74              
75 0 0       0 unless ($filename =~ /^(\S+):(.*)$/) {
76 0         0 INFO "Loading $filename";
77 0 0       0 my $parsed = LoadFile($filename) or LOGDIE "Invalid YAML : $filename\n";
78 0         0 return $parsed;
79             }
80              
81 0         0 my ($host,$file) = ($1,$2);
82 0         0 INFO "Loading remote file $file from $host";
83 0         0 my $errs = File::Temp->new();
84 0         0 my $content = `$Ssh $host cat $file 2>$errs`;
85 0 0       0 if ($?) {
86 0         0 LOGDIE "Error (code $?) running ssh $host cat $file : ".`tail -2 $errs`;
87             }
88 0 0       0 my $parsed = Load($content) or do {
89 0         0 ERROR "Invalid YAML: $filename";
90 0         0 return;
91             };
92 0         0 return $parsed;
93             }
94              
95             sub run {
96 6     6 1 6738 my $class = shift;
97 6         9 my $client = shift;
98 6 50       23 my @args = @_ ? @_ : @ARGV;
99 6         9 our $TESTING;
100              
101 6 50 33     32 return $class->_usage($client) if !$args[0] || $args[0] =~ /help/;
102              
103             # Preprocessing for any common args, e.g. --remote
104 6         7 my $arg;
105             ARG :
106 6         15 while ($arg = shift @args) {
107 6         14 for ($arg) {
108 6 50       9 /--remote/ and do {
109 0         0 my $remote = shift @args;
110 0         0 TRACE "Using remote $remote";
111 0         0 $client->remote($remote);
112 0         0 next ARG;
113             };
114 6         10 last ARG;
115             }
116             }
117              
118 6 50       26 my $method = $arg or $class->_usage($client);
119              
120             # Map some alternative command line forms.
121 6         8 my $try_stdin;
122 6 50       13 if ( $method eq 'create' ) {
123 0 0       0 $method = shift @args or $class->_usage( $client, "Missing " );
124 0         0 $try_stdin = 1;
125             }
126              
127 6 50       12 if ( $method =~ /^(delete|search)$/ ) { # e.g. search -> app_search
128 0         0 $method = ( shift @args ) . '_' . $method;
129             }
130              
131 6 50       34 unless ($client->can($method)) {
132 0         0 $class->_usage($client, "Unrecognized argument : $method");
133 0         0 return;
134             }
135              
136 6         31 my $meta = Clustericious::Client::Meta::Route->new(
137             route_name => $method,
138             client_class => ref $client
139             );
140              
141 6 100       72 if ($meta->get('args')) {
142             # No heuristics for args.
143              
144 5         72 my $obj = $client->$method({ command_line => 1 }, @args);
145              
146 3 50       41 ERROR $client->errorstring if $client->has_error;
147              
148             # Copied from below, until that code is deprecated.
149 3 50 33     228 if ( blessed($obj) && $obj->isa("Mojo::Transaction") ) {
    50 33        
    50 33        
      33        
      33        
150 0 0       0 if ( my $res = $obj->success ) {
151 0         0 print $res->code," ",$res->default_message,"\n";
152             } else {
153 0         0 my ( $message, $code ) = $obj->error;
154 0 0       0 ERROR $code if $code;
155 0         0 ERROR $message;
156             }
157             } elsif (ref $obj eq 'HASH' && keys %$obj == 1 && $obj->{text}) {
158 0         0 print $obj->{text};
159             } elsif ($client->tx && $client->tx->req->method eq 'POST' && $meta->get("quiet_post")) {
160 0         0 my $msg = $client->res->code." ".$client->res->default_message;
161 0         0 my $got = $client->res->json;
162 0 0 0     0 if ($got && ref $got eq 'HASH' and keys %$got==1 && $got->{text}) {
      0        
      0        
163 0         0 $msg .= " ($got->{text})";
164             }
165 0         0 INFO $msg;
166             } else {
167 3 50       46 print _prettyDump($obj) unless $TESTING;
168             }
169 3         15 return;
170             }
171              
172             # Code below here should be deprecated, these are various heuristics for argument processing.
173              
174 1         4 my @extra_args = ( '/dev/null' );
175 1         2 my $have_filenames;
176              
177             # Assume we have files and/or remote globs
178 1 50 33     5 if ( !$meta->get('dont_read_files') && @args > 0 && ( -r $_[-1] || $_[-1] =~ /^\S+:/ ) ) {
    50 33        
      33        
      33        
      33        
179 0         0 $have_filenames = 1;
180 0         0 @extra_args = ();
181 0         0 while (my $arg = pop @args) {
182 0 0       0 if ($arg =~ /^\S+:/) {
    0          
183 0         0 push @extra_args, _expand_remote_glob($arg);
184             } elsif (-e $arg) {
185 0         0 push @extra_args, $arg;
186             } else {
187 0         0 LOGDIE "Do not know how to interpret argument : $arg";
188             }
189             }
190             } elsif ( $try_stdin && (-r STDIN) && @args==0) {
191 0         0 my $content = join '', ;
192 0         0 $content = Load($content);
193 0 0       0 LOGDIE "Invalid yaml content in $method" unless $content;
194 0         0 push @args, $content;
195             }
196              
197             # Finally, run :
198 1         3 for my $arg (@extra_args) {
199 1         2 my $obj;
200 1 50       3 if ($have_filenames) {
201 0         0 $obj = $client->$method(@args, _load_yaml($arg));
202             } else {
203 1         4 $obj = $client->$method(@args);
204             }
205 1 50       15 ERROR $client->errorstring if $client->errorstring;
206 1 50       12 next unless $obj;
207              
208 1 50 33     35 if ( blessed($obj) && $obj->isa("Mojo::Transaction") ) {
    50 33        
    50 33        
      33        
      33        
209 0 0       0 if ( my $res = $obj->success ) {
210 0         0 print $res->code," ",$res->default_message,"\n";
211             } else {
212 0         0 my ( $message, $code ) = $obj->error;
213 0 0       0 ERROR $code if $code;
214 0         0 ERROR $message;
215             }
216             } elsif (ref $obj eq 'HASH' && keys %$obj == 1 && $obj->{text}) {
217 0         0 print $obj->{text};
218             } elsif ($client->tx && $client->tx->req->method eq 'POST' && $meta->get("quiet_post")) {
219 0         0 my $msg = $client->res->code." ".$client->res->default_message;
220 0         0 my $got = $client->res->json;
221 0 0 0     0 if ($got && ref $got eq 'HASH' and keys %$got==1 && $got->{text}) {
      0        
      0        
222 0         0 $msg .= " ($got->{text})";
223             }
224 0         0 INFO $msg;
225             } else {
226 1 50       14 print _prettyDump($obj) unless $TESTING;
227             }
228             }
229 1         6 return;
230             }
231              
232             sub _prettyDump {
233 0     0     my $what = shift;
234 0 0   0     rmap_ref { $_ = $_->iso8601() if ref($_) eq 'DateTime' } $what;
  0            
235 0           return Dump($what);
236             }
237              
238              
239             1;
240              
241             __END__