File Coverage

blib/lib/AWS/CLIWrapper.pm
Criterion Covered Total %
statement 126 540 23.3
branch 58 118 49.1
condition 15 51 29.4
subroutine 23 376 6.1
pod 353 363 97.2
total 575 1448 39.7


line stmt bran cond sub pod time code
1             package AWS::CLIWrapper;
2              
3 9     9   588249 use 5.008001;
  9         114  
4 9     9   70 use strict;
  9         16  
  9         183  
5 9     9   39 use warnings;
  9         14  
  9         398  
6              
7             our $VERSION = '1.27';
8              
9 9     9   4066 use version;
  9         17270  
  9         50  
10 9     9   6659 use JSON 2;
  9         105309  
  9         54  
11 9     9   7395 use IPC::Cmd;
  9         528900  
  9         853  
12 9     9   4468 use String::ShellQuote;
  9         7621  
  9         588  
13 9     9   85 use Carp;
  9         25  
  9         92196  
14              
15             our $Error = { Message => '', Code => '' };
16              
17             our $true = do { bless \(my $dummy = 1), "AWS::CLIWrapper::Boolean" };
18             our $false = do { bless \(my $dummy = 0), "AWS::CLIWrapper::Boolean" };
19              
20             my $AWSCLI_VERSION = undef;
21             my $DEFAULT_CATCH_ERROR_RETRIES = 3;
22             my $DEFAULT_CATCH_ERROR_MIN_DELAY = 3;
23             my $DEFAULT_CATCH_ERROR_MAX_DELAY = 10;
24              
25             sub new {
26 37     37 1 38853 my($class, %param) = @_;
27              
28 37         213 my $region = $param{region};
29              
30 37         78 my @opt = ();
31 37         93 for my $k (qw(region profile endpoint_url)) {
32 111 100       304 if (my $v = delete $param{$k}) {
33 1         6 push @opt, param2opt($k, $v);
34             }
35             }
36              
37             my $self = bless {
38             region => $region,
39             opt => \@opt,
40             json => JSON->new,
41             param => \%param,
42             awscli_path => $param{awscli_path} || 'aws',
43             croak_on_error => !!$param{croak_on_error},
44 37 100 100     576 timeout => (defined $ENV{AWS_CLIWRAPPER_TIMEOUT}) ? $ENV{AWS_CLIWRAPPER_TIMEOUT} : 30,
45             }, $class;
46              
47 37         125 return $self;
48             }
49              
50 1     1 0 11 sub region { shift->{region} }
51              
52             sub awscli_path {
53 10     10 0 32 my ($self) = @_;
54 10         61 return $self->{awscli_path};
55             }
56              
57             sub awscli_version {
58 5     5 0 31 my ($self) = @_;
59 5 100       24 unless (defined $AWSCLI_VERSION) {
60 3         4 $AWSCLI_VERSION = do {
61 3         13 my $awscli_path = $self->awscli_path;
62 3   100     16107 my $vs = qx($awscli_path --version 2>&1) || '';
63 3         45 my $v;
64 3 100       132 if ($vs =~ m{/([0-9.]+)\s}) {
65 1         29 $v = $1;
66             } else {
67 2         17 $v = 0;
68             }
69 3         214 version->parse($v);
70             };
71             }
72 5         160 return $AWSCLI_VERSION;
73             }
74              
75             sub catch_error_pattern {
76 9     9 0 42 my ($self) = @_;
77              
78             return $ENV{AWS_CLIWRAPPER_CATCH_ERROR_PATTERN}
79 9 100       57 if defined $ENV{AWS_CLIWRAPPER_CATCH_ERROR_PATTERN};
80              
81             return $self->{param}->{catch_error_pattern}
82 5 100       23 if defined $self->{param}->{catch_error_pattern};
83            
84 4         16 return;
85             }
86              
87             sub catch_error_retries {
88 7     7 0 36 my ($self) = @_;
89              
90             my $retries = defined $ENV{AWS_CLIWRAPPER_CATCH_ERROR_RETRIES}
91             ? $ENV{AWS_CLIWRAPPER_CATCH_ERROR_RETRIES}
92             : defined $self->{param}->{catch_error_retries}
93             ? $self->{param}->{catch_error_retries}
94 7 100       40 : $DEFAULT_CATCH_ERROR_RETRIES;
    100          
95              
96 7 100       28 $retries = $DEFAULT_CATCH_ERROR_RETRIES if $retries < 0;
97              
98 7         21 return $retries;
99             }
100              
101             sub catch_error_min_delay {
102 27     27 0 78 my ($self) = @_;
103              
104             my $min_delay = defined $ENV{AWS_CLIWRAPPER_CATCH_ERROR_MIN_DELAY}
105             ? $ENV{AWS_CLIWRAPPER_CATCH_ERROR_MIN_DELAY}
106             : defined $self->{param}->{catch_error_min_delay}
107             ? $self->{param}->{catch_error_min_delay}
108 27 100       97 : $DEFAULT_CATCH_ERROR_MIN_DELAY;
    100          
109            
110 27 100       82 $min_delay = $DEFAULT_CATCH_ERROR_MIN_DELAY if $min_delay < 0;
111              
112 27         70 return $min_delay;
113             }
114              
115             sub catch_error_max_delay {
116 14     14 0 58 my ($self) = @_;
117              
118 14         31 my $min_delay = $self->catch_error_min_delay;
119              
120             my $max_delay = defined $ENV{AWS_CLIWRAPPER_CATCH_ERROR_MAX_DELAY}
121             ? $ENV{AWS_CLIWRAPPER_CATCH_ERROR_MAX_DELAY}
122             : defined $self->{param}->{catch_error_max_delay}
123             ? $self->{param}->{catch_error_max_delay}
124 14 100       49 : $DEFAULT_CATCH_ERROR_MAX_DELAY;
    100          
125            
126 14 100       58 $max_delay = $DEFAULT_CATCH_ERROR_MAX_DELAY if $max_delay < 0;
127              
128 14 100       34 $max_delay = $min_delay if $min_delay > $max_delay;
129              
130 14         30 return $max_delay;
131             }
132              
133             sub catch_error_delay {
134 8     8 0 50 my ($self) = @_;
135              
136 8         44 my $min = $self->catch_error_min_delay;
137 8         36 my $max = $self->catch_error_max_delay;
138              
139 8 100       79 return $min == $max ? $min : $min + (int rand $max - $min);
140             }
141              
142             sub param2opt {
143 1     1 0 2 my($k, $v) = @_;
144              
145 1         2 my @v;
146              
147 1         3 $k =~ s/_/-/g;
148 1         3 $k = '--'.$k;
149              
150 1         3 my $type = ref $v;
151 1 50       4 if (! $type) {
    0          
    0          
    0          
152 1 50       3 if ($k eq '--output-file') {
153             # aws s3api get-object takes a single arg for output file path
154 0         0 return $v;
155             } else {
156 1         3 push @v, $v;
157             }
158             } elsif ($type eq 'ARRAY') {
159 0 0       0 push @v, map { ref($_) ? encode_json(_compat_kv($_)) : $_ } @$v;
  0         0  
160             } elsif ($type eq 'HASH') {
161 0         0 push @v, encode_json(_compat_kv($v));
162             } elsif ($type eq 'AWS::CLIWrapper::Boolean') {
163 0 0       0 if ($$v == 1) {
164 0         0 return ($k);
165             } else {
166 0         0 return ();
167             }
168             } else {
169 0         0 push @v, $v;
170             }
171              
172 1         4 return ($k, @v);
173             }
174              
175             # >= 0.14.0 : Key, Values, Value, Name
176             # < 0.14.0 : key, values, value, name
177             sub _compat_kv_uc {
178 0     0   0 my $v = shift;
179 0         0 my $type = ref $v;
180              
181 0 0 0     0 if ($type && $type eq 'HASH') {
182 0         0 for my $hk (keys %$v) {
183 0 0       0 if ($hk =~ /^(?:key|name|values|value)$/) {
184 0         0 $v->{ucfirst($hk)} = delete $v->{$hk};
185             }
186             }
187             }
188              
189 0         0 return $v;
190             }
191             # sub _compat_kv_lc {
192             # my $v = shift;
193             # my $type = ref $v;
194              
195             # if ($type && $type eq 'HASH') {
196             # for my $hk (keys %$v) {
197             # if ($hk =~ /^(?:Key|Name|Values|Values)$/) {
198             # $v->{lc($hk)} = delete $v->{$hk};
199             # }
200             # }
201             # }
202              
203             # return $v;
204             # }
205             # Drop support < 0.14.0 for preventing execute aws command in loading this module
206             *_compat_kv = *_compat_kv_uc;
207              
208 3     3 0 143 sub json { $_[0]->{json} }
209              
210             sub _execute {
211 5     5   13 my $self = shift;
212 5         14 my $service = shift;
213 5         12 my $operation = shift;
214 5         17 my @cmd = ($self->awscli_path, @{$self->{opt}}, $service, $operation);
  5         21  
215 5 50 33     28 if ($service eq 'ec2' && $operation eq 'wait') {
216 0         0 push(@cmd, shift @_);
217             }
218 5 50       21 if (ref($_[0]) eq 'ARRAY') {
219             # for s3 sync FROM TO
220 0         0 push @cmd, @{ shift @_ };
  0         0  
221             }
222 5         15 my($param, %opt) = @_;
223              
224 5 50 33     68 if ($service eq 'ec2' && $operation eq 'run-instances') {
    50 33        
    50 33        
225             # compat: ec2 run-instances
226             # >= 0.14.0 : --count N or --count MIN:MAX
227             # < 0.14.0 : --min-count N and --max-count N
228 0 0       0 if ($self->awscli_version >= 0.14.0) {
229 0         0 my($min,$max) = (1,1);
230 0         0 for my $hk (keys %$param) {
231 0 0       0 if ($hk eq 'min_count') {
    0          
232 0         0 $min = delete $param->{min_count};
233             } elsif ($hk eq 'max_count') {
234 0         0 $max = delete $param->{max_count};
235             }
236             }
237             $param->{count} = "${min}:${max}" unless $param->{count}
238 0 0       0 } else {
239 0         0 my($min,$max);
240 0         0 for my $hk (keys %$param) {
241 0 0       0 if ($hk eq 'count') {
242 0         0 ($min,$max) = split /:/, delete($param->{count});
243 0   0     0 $max ||= $min;
244 0         0 last;
245             }
246             }
247 0 0       0 $param->{min_count} = $min unless $param->{min_count};
248 0 0       0 $param->{max_count} = $max unless $param->{max_count};
249             }
250             } elsif ($service eq 's3' && $self->awscli_version >= 0.15.0) {
251 0 0       0 if ($operation !~ /^(?:cp|ls|mb|mv|rb|rm|sync|website)$/) {
252 0         0 return $self->s3api($operation, @_);
253             }
254             } elsif ($service eq 's3api' && $self->awscli_version < 0.15.0) {
255 0         0 return $self->s3($operation, @_);
256             }
257              
258 5         45 while (my($k, $v) = each %$param) {
259 0         0 my @o = param2opt($k, $v);
260 0 0 0     0 if ($service eq 's3' && $k =~ /^(?:include|exclude)$/) {
261 0         0 my $optk = shift @o;
262 0         0 @o = map { $optk => $_ } @o;
  0         0  
263             }
264 0         0 push @cmd, @o;
265             }
266 5         13 @cmd = map { shell_quote($_) } @cmd;
  15         553  
267 5 50       157 warn "cmd: ".join(' ', @cmd) if $ENV{AWSCLI_DEBUG};
268              
269 5         58 my $error_re = $self->catch_error_pattern;
270 5 100       30 my $retries = $error_re ? $self->catch_error_retries : 0;
271              
272             RETRY: {
273 5         11 $Error = { Message => '', Code => '' };
  10         50  
274              
275 10         77 my $exit_value = $self->_run(\%opt, \@cmd);
276 10         150 my $ret = $self->_handle($service, $operation, $exit_value);
277              
278 10 100       142 return $ret unless $Error->{Code};
279              
280 7 100 66     201 if ($retries-- > 0 and $Error->{Message} =~ $error_re) {
281 5         51 my $delay = $self->catch_error_delay;
282              
283             warn "Caught error matching $error_re, sleeping $delay seconds before retrying\n"
284 5 50       22 if $ENV{AWSCLI_DEBUG};
285              
286 5         409 sleep $delay;
287              
288 5         47 redo RETRY;
289             }
290              
291 2 100       483 croak $Error->{Message} if $self->{croak_on_error};
292              
293 1         31 return $ret;
294             }
295             }
296              
297             sub _run {
298 10     10   47 my ($self, $opt, $cmd) = @_;
299              
300 10         31 my $ret;
301 10 50 33     47 if (exists $opt->{'nofork'} && $opt->{'nofork'}) {
302             # better for perl debugger
303             my($ok, $err, $buf, $stdout_buf, $stderr_buf) = IPC::Cmd::run(
304             command => join(' ', @$cmd),
305             timeout => $opt->{timeout} || $self->{timeout},
306 0   0     0 );
307 0         0 $ret->{stdout} = join "", @$stdout_buf;
308 0 0       0 $ret->{err_msg} = (defined $err ? "$err\n" : "") . join "", @$stderr_buf;
309 0 0       0 if ($ok) {
310 0         0 $ret->{exit_code} = 0;
311 0         0 $ret->{timeout} = 0;
312             } else {
313 0         0 $ret->{exit_code} = 2;
314 0 0 0     0 $ret->{timeout} = 1 if defined $err && $err =~ /^IPC::Cmd::TimeOut:/;
315             }
316 0         0 print "";
317             } else {
318             $ret = IPC::Cmd::run_forked(join(' ', @$cmd), {
319             timeout => $opt->{timeout} || $self->{timeout},
320 10   33     170 });
321             }
322              
323 10         148906 return $ret;
324             }
325              
326             sub _handle {
327 10     10   138 my ($self, $service, $operation, $ret) = @_;
328              
329 10 100 66     132 if ($ret->{exit_code} == 0 && $ret->{timeout} == 0) {
330 3         18 my $json = $ret->{stdout};
331             warn sprintf("%s.%s[%s]: %s\n",
332             $service, $operation, 'OK', $json,
333 3 50       21 ) if $ENV{AWSCLI_DEBUG};
334 3         11 local $@;
335 3         13 my($ret) = eval {
336             # aws s3 returns null HTTP body, so failed to parse as JSON
337              
338             # Temporary disable __DIE__ handler to prevent the
339             # exception from decode() from catching by outer
340             # __DIE__ handler.
341 3     0   115 local $SIG{__DIE__} = sub {};
342              
343 3         34 $self->json->decode($json);
344             };
345 3 50       28 if ($@) {
346 0 0       0 if ($ENV{AWSCLI_DEBUG}) {
347 0         0 warn $@;
348 0         0 warn qq|stdout: "$ret->{stdout}"|;
349 0         0 warn qq|err_msg: "$ret->{err_msg}"|;
350             }
351 0   0     0 return $json || 'success';
352             }
353 3         13 return $ret;
354             } else {
355 7         17 my $stdout_str = $ret->{stdout};
356 7 50 33     71 if ($stdout_str && $stdout_str =~ /^{/) {
357 0         0 my $json = $stdout_str;
358             warn sprintf("%s.%s[%s]: %s\n",
359             $service, $operation, 'NG', $json,
360 0 0       0 ) if $ENV{AWSCLI_DEBUG};
361 0         0 my($ret) = $self->json->decode_prefix($json);
362 0 0 0     0 if (exists $ret->{Errors} && ref($ret->{Errors}) eq 'ARRAY') {
    0          
363 0         0 $Error = $ret->{Errors}[0];
364             } elsif (exists $ret->{Response}{Errors}{Error}) {
365             # old structure (maybe botocore < 0.7.0)
366 0         0 $Error = $ret->{Response}{Errors}{Error};
367             } else {
368 0         0 $Error = { Message => 'Unknown', Code => 'Unknown' };
369             }
370             } else {
371 7         68 my $msg = $ret->{err_msg};
372             warn sprintf("%s.%s[%s]: %s\n",
373             $service, $operation, 'NG', $msg,
374 7 50       52 ) if $ENV{AWSCLI_DEBUG};
375 7         144 $Error = { Message => $msg, Code => 'Unknown' };
376             }
377              
378 7         32 return;
379             }
380             }
381              
382             # aws help | col -b | perl -ne 'if (/^AVAILABLE/.../^[A-Z]/) { s/^\s+o\s+// or next; chomp; next if $_ eq 'help'; my $sn = $_; $sn =~ s/-/_/g; printf "sub %-18s { shift->_execute('"'"'%s'"'"', \@_) }\n", $sn, $_ }'
383             # aws help | col -b | perl -ne 'if (/^AVAILABLE/.../^[A-Z]/) { s/^\s+o\s+// or next; chomp; next if $_ eq 'help'; my $sn = $_; $sn =~ s/-/_/g; printf "=item B<%s>(\$operation:Str, \$param:HashRef, %%opt:Hash)\n\n", $sn}'
384             # =item B($operation:Str, $path:ArrayRef, $param:HashRef, %opt:Hash)
385 0     0 1 0 sub accessanalyzer { shift->_execute('accessanalyzer', @_) }
386 0     0 1 0 sub account { shift->_execute('account', @_) }
387 0     0 1 0 sub acm { shift->_execute('acm', @_) }
388 0     0 1 0 sub acm_pca { shift->_execute('acm-pca', @_) }
389 0     0 1 0 sub alexaforbusiness { shift->_execute('alexaforbusiness', @_) }
390 0     0 1 0 sub amp { shift->_execute('amp', @_) }
391 0     0 1 0 sub amplify { shift->_execute('amplify', @_) }
392 0     0 1 0 sub amplifybackend { shift->_execute('amplifybackend', @_) }
393 0     0 1 0 sub amplifyuibuilder { shift->_execute('amplifyuibuilder', @_) }
394 0     0 1 0 sub apigateway { shift->_execute('apigateway', @_) }
395 0     0 1 0 sub apigatewaymanagementapi { shift->_execute('apigatewaymanagementapi', @_) }
396 0     0 1 0 sub apigatewayv2 { shift->_execute('apigatewayv2', @_) }
397 0     0 1 0 sub appconfig { shift->_execute('appconfig', @_) }
398 0     0 1 0 sub appconfigdata { shift->_execute('appconfigdata', @_) }
399 0     0 1 0 sub appfabric { shift->_execute('appfabric', @_) }
400 0     0 1 0 sub appflow { shift->_execute('appflow', @_) }
401 0     0 1 0 sub appintegrations { shift->_execute('appintegrations', @_) }
402 0     0 1 0 sub application_autoscaling { shift->_execute('application-autoscaling', @_) }
403 0     0 1 0 sub application_insights { shift->_execute('application-insights', @_) }
404 0     0 1 0 sub applicationcostprofiler { shift->_execute('applicationcostprofiler', @_) }
405 0     0 1 0 sub appmesh { shift->_execute('appmesh', @_) }
406 0     0 1 0 sub apprunner { shift->_execute('apprunner', @_) }
407 0     0 1 0 sub appstream { shift->_execute('appstream', @_) }
408 0     0 1 0 sub appsync { shift->_execute('appsync', @_) }
409 0     0 1 0 sub arc_zonal_shift { shift->_execute('arc-zonal-shift', @_) }
410 0     0 1 0 sub athena { shift->_execute('athena', @_) }
411 0     0 1 0 sub auditmanager { shift->_execute('auditmanager', @_) }
412 0     0 1 0 sub autoscaling { shift->_execute('autoscaling', @_) }
413 0     0 1 0 sub autoscaling_plans { shift->_execute('autoscaling-plans', @_) }
414 0     0 1 0 sub backup { shift->_execute('backup', @_) }
415 0     0 1 0 sub backup_gateway { shift->_execute('backup-gateway', @_) }
416 0     0 1 0 sub backupstorage { shift->_execute('backupstorage', @_) }
417 0     0 1 0 sub batch { shift->_execute('batch', @_) }
418 0     0 1 0 sub billingconductor { shift->_execute('billingconductor', @_) }
419 0     0 1 0 sub braket { shift->_execute('braket', @_) }
420 0     0 1 0 sub budgets { shift->_execute('budgets', @_) }
421 0     0 1 0 sub ce { shift->_execute('ce', @_) }
422 0     0 1 0 sub chime { shift->_execute('chime', @_) }
423 0     0 1 0 sub chime_sdk_identity { shift->_execute('chime-sdk-identity', @_) }
424 0     0 1 0 sub chime_sdk_media_pipelines { shift->_execute('chime-sdk-media-pipelines', @_) }
425 0     0 1 0 sub chime_sdk_meetings { shift->_execute('chime-sdk-meetings', @_) }
426 0     0 1 0 sub chime_sdk_messaging { shift->_execute('chime-sdk-messaging', @_) }
427 0     0 1 0 sub chime_sdk_voice { shift->_execute('chime-sdk-voice', @_) }
428 0     0 1 0 sub cleanrooms { shift->_execute('cleanrooms', @_) }
429 0     0 1 0 sub cloud9 { shift->_execute('cloud9', @_) }
430 0     0 1 0 sub cloudcontrol { shift->_execute('cloudcontrol', @_) }
431 0     0 1 0 sub clouddirectory { shift->_execute('clouddirectory', @_) }
432 0     0 1 0 sub cloudformation { shift->_execute('cloudformation', @_) }
433 0     0 1 0 sub cloudfront { shift->_execute('cloudfront', @_) }
434 0     0 1 0 sub cloudhsm { shift->_execute('cloudhsm', @_) }
435 0     0 1 0 sub cloudhsmv2 { shift->_execute('cloudhsmv2', @_) }
436 0     0 1 0 sub cloudsearch { shift->_execute('cloudsearch', @_) }
437 0     0 1 0 sub cloudsearchdomain { shift->_execute('cloudsearchdomain', @_) }
438 0     0 1 0 sub cloudtrail { shift->_execute('cloudtrail', @_) }
439 0     0 1 0 sub cloudtrail_data { shift->_execute('cloudtrail-data', @_) }
440 0     0 1 0 sub cloudwatch { shift->_execute('cloudwatch', @_) }
441 0     0 1 0 sub codeartifact { shift->_execute('codeartifact', @_) }
442 0     0 1 0 sub codebuild { shift->_execute('codebuild', @_) }
443 0     0 1 0 sub codecatalyst { shift->_execute('codecatalyst', @_) }
444 0     0 1 0 sub codecommit { shift->_execute('codecommit', @_) }
445 0     0 1 0 sub codeguru_reviewer { shift->_execute('codeguru-reviewer', @_) }
446 0     0 1 0 sub codeguru_security { shift->_execute('codeguru-security', @_) }
447 0     0 1 0 sub codeguruprofiler { shift->_execute('codeguruprofiler', @_) }
448 0     0 1 0 sub codepipeline { shift->_execute('codepipeline', @_) }
449 0     0 1 0 sub codestar { shift->_execute('codestar', @_) }
450 0     0 1 0 sub codestar_connections { shift->_execute('codestar-connections', @_) }
451 0     0 1 0 sub codestar_notifications { shift->_execute('codestar-notifications', @_) }
452 0     0 1 0 sub cognito_identity { shift->_execute('cognito-identity', @_) }
453 0     0 1 0 sub cognito_idp { shift->_execute('cognito-idp', @_) }
454 0     0 1 0 sub cognito_sync { shift->_execute('cognito-sync', @_) }
455 0     0 1 0 sub comprehend { shift->_execute('comprehend', @_) }
456 0     0 1 0 sub comprehendmedical { shift->_execute('comprehendmedical', @_) }
457 0     0 1 0 sub compute_optimizer { shift->_execute('compute-optimizer', @_) }
458 0     0 1 0 sub configservice { shift->_execute('configservice', @_) }
459 0     0 1 0 sub configure { shift->_execute('configure', @_) }
460 0     0 1 0 sub connect { shift->_execute('connect', @_) }
461 0     0 1 0 sub connect_contact_lens { shift->_execute('connect-contact-lens', @_) }
462 0     0 1 0 sub connectcampaigns { shift->_execute('connectcampaigns', @_) }
463 0     0 1 0 sub connectcases { shift->_execute('connectcases', @_) }
464 0     0 1 0 sub connectparticipant { shift->_execute('connectparticipant', @_) }
465 0     0 1 0 sub controltower { shift->_execute('controltower', @_) }
466 0     0 1 0 sub cur { shift->_execute('cur', @_) }
467 0     0 1 0 sub customer_profiles { shift->_execute('customer-profiles', @_) }
468 0     0 1 0 sub databrew { shift->_execute('databrew', @_) }
469 0     0 1 0 sub dataexchange { shift->_execute('dataexchange', @_) }
470 0     0 1 0 sub datapipeline { shift->_execute('datapipeline', @_) }
471 0     0 1 0 sub datasync { shift->_execute('datasync', @_) }
472 0     0 1 0 sub dax { shift->_execute('dax', @_) }
473 0     0 1 0 sub deploy { shift->_execute('deploy', @_) }
474 0     0 1 0 sub detective { shift->_execute('detective', @_) }
475 0     0 1 0 sub devicefarm { shift->_execute('devicefarm', @_) }
476 0     0 1 0 sub devops_guru { shift->_execute('devops-guru', @_) }
477 0     0 1 0 sub directconnect { shift->_execute('directconnect', @_) }
478 0     0 1 0 sub discovery { shift->_execute('discovery', @_) }
479 0     0 1 0 sub dlm { shift->_execute('dlm', @_) }
480 0     0 1 0 sub dms { shift->_execute('dms', @_) }
481 0     0 1 0 sub docdb { shift->_execute('docdb', @_) }
482 0     0 1 0 sub docdb_elastic { shift->_execute('docdb-elastic', @_) }
483 0     0 1 0 sub drs { shift->_execute('drs', @_) }
484 0     0 1 0 sub ds { shift->_execute('ds', @_) }
485 0     0 1 0 sub dynamodb { shift->_execute('dynamodb', @_) }
486 0     0 1 0 sub dynamodbstreams { shift->_execute('dynamodbstreams', @_) }
487 0     0 1 0 sub ebs { shift->_execute('ebs', @_) }
488 0     0 1 0 sub ec2 { shift->_execute('ec2', @_) }
489 0     0 1 0 sub ec2_instance_connect { shift->_execute('ec2-instance-connect', @_) }
490 0     0 1 0 sub ecr { shift->_execute('ecr', @_) }
491 0     0 1 0 sub ecr_public { shift->_execute('ecr-public', @_) }
492 5     5 1 92 sub ecs { shift->_execute('ecs', @_) }
493 0     0 1   sub efs { shift->_execute('efs', @_) }
494 0     0 1   sub eks { shift->_execute('eks', @_) }
495 0     0 1   sub elastic_inference { shift->_execute('elastic-inference', @_) }
496 0     0 1   sub elasticache { shift->_execute('elasticache', @_) }
497 0     0 1   sub elasticbeanstalk { shift->_execute('elasticbeanstalk', @_) }
498 0     0 1   sub elastictranscoder { shift->_execute('elastictranscoder', @_) }
499 0     0 1   sub elb { shift->_execute('elb', @_) }
500 0     0 1   sub elbv2 { shift->_execute('elbv2', @_) }
501 0     0 1   sub emr { shift->_execute('emr', @_) }
502 0     0 1   sub emr_containers { shift->_execute('emr-containers', @_) }
503 0     0 1   sub emr_serverless { shift->_execute('emr-serverless', @_) }
504 0     0 1   sub es { shift->_execute('es', @_) }
505 0     0 1   sub events { shift->_execute('events', @_) }
506 0     0 1   sub evidently { shift->_execute('evidently', @_) }
507 0     0 1   sub finspace { shift->_execute('finspace', @_) }
508 0     0 1   sub finspace_data { shift->_execute('finspace-data', @_) }
509 0     0 1   sub firehose { shift->_execute('firehose', @_) }
510 0     0 1   sub fis { shift->_execute('fis', @_) }
511 0     0 1   sub fms { shift->_execute('fms', @_) }
512 0     0 1   sub forecast { shift->_execute('forecast', @_) }
513 0     0 1   sub forecastquery { shift->_execute('forecastquery', @_) }
514 0     0 1   sub frauddetector { shift->_execute('frauddetector', @_) }
515 0     0 1   sub fsx { shift->_execute('fsx', @_) }
516 0     0 1   sub gamelift { shift->_execute('gamelift', @_) }
517 0     0 1   sub gamesparks { shift->_execute('gamesparks', @_) }
518 0     0 1   sub glacier { shift->_execute('glacier', @_) }
519 0     0 1   sub globalaccelerator { shift->_execute('globalaccelerator', @_) }
520 0     0 1   sub glue { shift->_execute('glue', @_) }
521 0     0 1   sub grafana { shift->_execute('grafana', @_) }
522 0     0 1   sub greengrass { shift->_execute('greengrass', @_) }
523 0     0 1   sub greengrassv2 { shift->_execute('greengrassv2', @_) }
524 0     0 1   sub groundstation { shift->_execute('groundstation', @_) }
525 0     0 1   sub guardduty { shift->_execute('guardduty', @_) }
526 0     0 1   sub health { shift->_execute('health', @_) }
527 0     0 1   sub healthlake { shift->_execute('healthlake', @_) }
528 0     0 1   sub history { shift->_execute('history', @_) }
529 0     0 1   sub honeycode { shift->_execute('honeycode', @_) }
530 0     0 1   sub iam { shift->_execute('iam', @_) }
531 0     0 1   sub identitystore { shift->_execute('identitystore', @_) }
532 0     0 1   sub imagebuilder { shift->_execute('imagebuilder', @_) }
533 0     0 1   sub importexport { shift->_execute('importexport', @_) }
534 0     0 1   sub inspector { shift->_execute('inspector', @_) }
535 0     0 1   sub inspector2 { shift->_execute('inspector2', @_) }
536 0     0 1   sub internetmonitor { shift->_execute('internetmonitor', @_) }
537 0     0 1   sub iot { shift->_execute('iot', @_) }
538 0     0 1   sub iot_data { shift->_execute('iot-data', @_) }
539 0     0 1   sub iot_jobs_data { shift->_execute('iot-jobs-data', @_) }
540 0     0 1   sub iot_roborunner { shift->_execute('iot-roborunner', @_) }
541 0     0 1   sub iot1click_devices { shift->_execute('iot1click-devices', @_) }
542 0     0 1   sub iot1click_projects { shift->_execute('iot1click-projects', @_) }
543 0     0 1   sub iotanalytics { shift->_execute('iotanalytics', @_) }
544 0     0 1   sub iotdeviceadvisor { shift->_execute('iotdeviceadvisor', @_) }
545 0     0 1   sub iotevents { shift->_execute('iotevents', @_) }
546 0     0 1   sub iotevents_data { shift->_execute('iotevents-data', @_) }
547 0     0 1   sub iotfleethub { shift->_execute('iotfleethub', @_) }
548 0     0 1   sub iotfleetwise { shift->_execute('iotfleetwise', @_) }
549 0     0 1   sub iotsecuretunneling { shift->_execute('iotsecuretunneling', @_) }
550 0     0 1   sub iotsitewise { shift->_execute('iotsitewise', @_) }
551 0     0 1   sub iotthingsgraph { shift->_execute('iotthingsgraph', @_) }
552 0     0 1   sub iottwinmaker { shift->_execute('iottwinmaker', @_) }
553 0     0 1   sub iotwireless { shift->_execute('iotwireless', @_) }
554 0     0 1   sub ivs { shift->_execute('ivs', @_) }
555 0     0 1   sub ivs_realtime { shift->_execute('ivs-realtime', @_) }
556 0     0 1   sub ivschat { shift->_execute('ivschat', @_) }
557 0     0 1   sub kafka { shift->_execute('kafka', @_) }
558 0     0 1   sub kafkaconnect { shift->_execute('kafkaconnect', @_) }
559 0     0 1   sub kendra { shift->_execute('kendra', @_) }
560 0     0 1   sub kendra_ranking { shift->_execute('kendra-ranking', @_) }
561 0     0 1   sub keyspaces { shift->_execute('keyspaces', @_) }
562 0     0 1   sub kinesis { shift->_execute('kinesis', @_) }
563 0     0 1   sub kinesis_video_archived_media { shift->_execute('kinesis-video-archived-media', @_) }
564 0     0 1   sub kinesis_video_media { shift->_execute('kinesis-video-media', @_) }
565 0     0 1   sub kinesis_video_signaling { shift->_execute('kinesis-video-signaling', @_) }
566 0     0 1   sub kinesis_video_webrtc_storage { shift->_execute('kinesis-video-webrtc-storage', @_) }
567 0     0 1   sub kinesisanalytics { shift->_execute('kinesisanalytics', @_) }
568 0     0 1   sub kinesisanalyticsv2 { shift->_execute('kinesisanalyticsv2', @_) }
569 0     0 1   sub kinesisvideo { shift->_execute('kinesisvideo', @_) }
570 0     0 1   sub kms { shift->_execute('kms', @_) }
571 0     0 1   sub lakeformation { shift->_execute('lakeformation', @_) }
572 0     0 1   sub lambda { shift->_execute('lambda', @_) }
573 0     0 1   sub lex_models { shift->_execute('lex-models', @_) }
574 0     0 1   sub lex_runtime { shift->_execute('lex-runtime', @_) }
575 0     0 1   sub lexv2_models { shift->_execute('lexv2-models', @_) }
576 0     0 1   sub lexv2_runtime { shift->_execute('lexv2-runtime', @_) }
577 0     0 1   sub license_manager { shift->_execute('license-manager', @_) }
578 0     0 1   sub license_manager_linux_subscriptions { shift->_execute('license-manager-linux-subscriptions', @_) }
579 0     0 1   sub license_manager_user_subscriptions { shift->_execute('license-manager-user-subscriptions', @_) }
580 0     0 1   sub lightsail { shift->_execute('lightsail', @_) }
581 0     0 1   sub location { shift->_execute('location', @_) }
582 0     0 1   sub logs { shift->_execute('logs', @_) }
583 0     0 1   sub lookoutequipment { shift->_execute('lookoutequipment', @_) }
584 0     0 1   sub lookoutmetrics { shift->_execute('lookoutmetrics', @_) }
585 0     0 1   sub lookoutvision { shift->_execute('lookoutvision', @_) }
586 0     0 1   sub m2 { shift->_execute('m2', @_) }
587 0     0 1   sub machinelearning { shift->_execute('machinelearning', @_) }
588 0     0 1   sub macie { shift->_execute('macie', @_) }
589 0     0 1   sub macie2 { shift->_execute('macie2', @_) }
590 0     0 1   sub managedblockchain { shift->_execute('managedblockchain', @_) }
591 0     0 1   sub marketplace_catalog { shift->_execute('marketplace-catalog', @_) }
592 0     0 1   sub marketplace_entitlement { shift->_execute('marketplace-entitlement', @_) }
593 0     0 1   sub marketplacecommerceanalytics { shift->_execute('marketplacecommerceanalytics', @_) }
594 0     0 1   sub mediaconnect { shift->_execute('mediaconnect', @_) }
595 0     0 1   sub mediaconvert { shift->_execute('mediaconvert', @_) }
596 0     0 1   sub medialive { shift->_execute('medialive', @_) }
597 0     0 1   sub mediapackage { shift->_execute('mediapackage', @_) }
598 0     0 1   sub mediapackage_vod { shift->_execute('mediapackage-vod', @_) }
599 0     0 1   sub mediapackagev2 { shift->_execute('mediapackagev2', @_) }
600 0     0 1   sub mediastore { shift->_execute('mediastore', @_) }
601 0     0 1   sub mediastore_data { shift->_execute('mediastore-data', @_) }
602 0     0 1   sub mediatailor { shift->_execute('mediatailor', @_) }
603 0     0 1   sub memorydb { shift->_execute('memorydb', @_) }
604 0     0 1   sub meteringmarketplace { shift->_execute('meteringmarketplace', @_) }
605 0     0 1   sub mgh { shift->_execute('mgh', @_) }
606 0     0 1   sub mgn { shift->_execute('mgn', @_) }
607 0     0 1   sub migration_hub_refactor_spaces { shift->_execute('migration-hub-refactor-spaces', @_) }
608 0     0 1   sub migrationhub_config { shift->_execute('migrationhub-config', @_) }
609 0     0 1   sub migrationhuborchestrator { shift->_execute('migrationhuborchestrator', @_) }
610 0     0 1   sub migrationhubstrategy { shift->_execute('migrationhubstrategy', @_) }
611 0     0 1   sub mobile { shift->_execute('mobile', @_) }
612 0     0 1   sub mq { shift->_execute('mq', @_) }
613 0     0 1   sub mturk { shift->_execute('mturk', @_) }
614 0     0 1   sub mwaa { shift->_execute('mwaa', @_) }
615 0     0 1   sub neptune { shift->_execute('neptune', @_) }
616 0     0 1   sub network_firewall { shift->_execute('network-firewall', @_) }
617 0     0 1   sub networkmanager { shift->_execute('networkmanager', @_) }
618 0     0 1   sub nimble { shift->_execute('nimble', @_) }
619 0     0 1   sub oam { shift->_execute('oam', @_) }
620 0     0 1   sub omics { shift->_execute('omics', @_) }
621 0     0 1   sub opensearch { shift->_execute('opensearch', @_) }
622 0     0 1   sub opensearchserverless { shift->_execute('opensearchserverless', @_) }
623 0     0 1   sub opsworks { shift->_execute('opsworks', @_) }
624 0     0 1   sub opsworks_cm { shift->_execute('opsworks-cm', @_) }
625 0     0 1   sub organizations { shift->_execute('organizations', @_) }
626 0     0 1   sub osis { shift->_execute('osis', @_) }
627 0     0 1   sub outposts { shift->_execute('outposts', @_) }
628 0     0 1   sub panorama { shift->_execute('panorama', @_) }
629 0     0 1   sub payment_cryptography { shift->_execute('payment-cryptography', @_) }
630 0     0 1   sub payment_cryptography_data { shift->_execute('payment-cryptography-data', @_) }
631 0     0 1   sub personalize { shift->_execute('personalize', @_) }
632 0     0 1   sub personalize_events { shift->_execute('personalize-events', @_) }
633 0     0 1   sub personalize_runtime { shift->_execute('personalize-runtime', @_) }
634 0     0 1   sub pi { shift->_execute('pi', @_) }
635 0     0 1   sub pinpoint { shift->_execute('pinpoint', @_) }
636 0     0 1   sub pinpoint_email { shift->_execute('pinpoint-email', @_) }
637 0     0 1   sub pinpoint_sms_voice { shift->_execute('pinpoint-sms-voice', @_) }
638 0     0 1   sub pinpoint_sms_voice_v2 { shift->_execute('pinpoint-sms-voice-v2', @_) }
639 0     0 1   sub pipes { shift->_execute('pipes', @_) }
640 0     0 1   sub polly { shift->_execute('polly', @_) }
641 0     0 1   sub pricing { shift->_execute('pricing', @_) }
642 0     0 1   sub privatenetworks { shift->_execute('privatenetworks', @_) }
643 0     0 1   sub proton { shift->_execute('proton', @_) }
644 0     0 1   sub qldb { shift->_execute('qldb', @_) }
645 0     0 1   sub qldb_session { shift->_execute('qldb-session', @_) }
646 0     0 1   sub quicksight { shift->_execute('quicksight', @_) }
647 0     0 1   sub ram { shift->_execute('ram', @_) }
648 0     0 1   sub rbin { shift->_execute('rbin', @_) }
649 0     0 1   sub rds { shift->_execute('rds', @_) }
650 0     0 1   sub rds_data { shift->_execute('rds-data', @_) }
651 0     0 1   sub redshift { shift->_execute('redshift', @_) }
652 0     0 1   sub redshift_data { shift->_execute('redshift-data', @_) }
653 0     0 1   sub redshift_serverless { shift->_execute('redshift-serverless', @_) }
654 0     0 1   sub rekognition { shift->_execute('rekognition', @_) }
655 0     0 1   sub resiliencehub { shift->_execute('resiliencehub', @_) }
656 0     0 1   sub resource_explorer_2 { shift->_execute('resource-explorer-2', @_) }
657 0     0 1   sub resource_groups { shift->_execute('resource-groups', @_) }
658 0     0 1   sub resourcegroupstaggingapi { shift->_execute('resourcegroupstaggingapi', @_) }
659 0     0 1   sub robomaker { shift->_execute('robomaker', @_) }
660 0     0 1   sub rolesanywhere { shift->_execute('rolesanywhere', @_) }
661 0     0 1   sub route53 { shift->_execute('route53', @_) }
662 0     0 1   sub route53_recovery_cluster { shift->_execute('route53-recovery-cluster', @_) }
663 0     0 1   sub route53_recovery_control_config { shift->_execute('route53-recovery-control-config', @_) }
664 0     0 1   sub route53_recovery_readiness { shift->_execute('route53-recovery-readiness', @_) }
665 0     0 1   sub route53domains { shift->_execute('route53domains', @_) }
666 0     0 1   sub route53resolver { shift->_execute('route53resolver', @_) }
667 0     0 1   sub rum { shift->_execute('rum', @_) }
668 0     0 1   sub s3 { shift->_execute('s3', @_) }
669 0     0 1   sub s3api { shift->_execute('s3api', @_) }
670 0     0 1   sub s3control { shift->_execute('s3control', @_) }
671 0     0 1   sub s3outposts { shift->_execute('s3outposts', @_) }
672 0     0 1   sub sagemaker { shift->_execute('sagemaker', @_) }
673 0     0 1   sub sagemaker_a2i_runtime { shift->_execute('sagemaker-a2i-runtime', @_) }
674 0     0 1   sub sagemaker_edge { shift->_execute('sagemaker-edge', @_) }
675 0     0 1   sub sagemaker_featurestore_runtime { shift->_execute('sagemaker-featurestore-runtime', @_) }
676 0     0 1   sub sagemaker_geospatial { shift->_execute('sagemaker-geospatial', @_) }
677 0     0 1   sub sagemaker_metrics { shift->_execute('sagemaker-metrics', @_) }
678 0     0 1   sub sagemaker_runtime { shift->_execute('sagemaker-runtime', @_) }
679 0     0 1   sub savingsplans { shift->_execute('savingsplans', @_) }
680 0     0 1   sub scheduler { shift->_execute('scheduler', @_) }
681 0     0 1   sub schemas { shift->_execute('schemas', @_) }
682 0     0 1   sub sdb { shift->_execute('sdb', @_) }
683 0     0 1   sub secretsmanager { shift->_execute('secretsmanager', @_) }
684 0     0 1   sub securityhub { shift->_execute('securityhub', @_) }
685 0     0 1   sub securitylake { shift->_execute('securitylake', @_) }
686 0     0 1   sub serverlessrepo { shift->_execute('serverlessrepo', @_) }
687 0     0 1   sub service_quotas { shift->_execute('service-quotas', @_) }
688 0     0 1   sub servicecatalog { shift->_execute('servicecatalog', @_) }
689 0     0 1   sub servicecatalog_appregistry { shift->_execute('servicecatalog-appregistry', @_) }
690 0     0 1   sub servicediscovery { shift->_execute('servicediscovery', @_) }
691 0     0 1   sub ses { shift->_execute('ses', @_) }
692 0     0 1   sub sesv2 { shift->_execute('sesv2', @_) }
693 0     0 1   sub shield { shift->_execute('shield', @_) }
694 0     0 1   sub signer { shift->_execute('signer', @_) }
695 0     0 1   sub simspaceweaver { shift->_execute('simspaceweaver', @_) }
696 0     0 1   sub sms { shift->_execute('sms', @_) }
697 0     0 1   sub snow_device_management { shift->_execute('snow-device-management', @_) }
698 0     0 1   sub snowball { shift->_execute('snowball', @_) }
699 0     0 1   sub sns { shift->_execute('sns', @_) }
700 0     0 1   sub sqs { shift->_execute('sqs', @_) }
701 0     0 1   sub ssm { shift->_execute('ssm', @_) }
702 0     0 1   sub ssm_contacts { shift->_execute('ssm-contacts', @_) }
703 0     0 1   sub ssm_incidents { shift->_execute('ssm-incidents', @_) }
704 0     0 1   sub ssm_sap { shift->_execute('ssm-sap', @_) }
705 0     0 1   sub sso { shift->_execute('sso', @_) }
706 0     0 1   sub sso_admin { shift->_execute('sso-admin', @_) }
707 0     0 1   sub sso_oidc { shift->_execute('sso-oidc', @_) }
708 0     0 1   sub stepfunctions { shift->_execute('stepfunctions', @_) }
709 0     0 1   sub storagegateway { shift->_execute('storagegateway', @_) }
710 0     0 1   sub sts { shift->_execute('sts', @_) }
711 0     0 1   sub support { shift->_execute('support', @_) }
712 0     0 1   sub support_app { shift->_execute('support-app', @_) }
713 0     0 1   sub swf { shift->_execute('swf', @_) }
714 0     0 1   sub synthetics { shift->_execute('synthetics', @_) }
715 0     0 1   sub textract { shift->_execute('textract', @_) }
716 0     0 1   sub timestream_query { shift->_execute('timestream-query', @_) }
717 0     0 1   sub timestream_write { shift->_execute('timestream-write', @_) }
718 0     0 1   sub tnb { shift->_execute('tnb', @_) }
719 0     0 1   sub transcribe { shift->_execute('transcribe', @_) }
720 0     0 1   sub transfer { shift->_execute('transfer', @_) }
721 0     0 1   sub translate { shift->_execute('translate', @_) }
722 0     0 1   sub verifiedpermissions { shift->_execute('verifiedpermissions', @_) }
723 0     0 1   sub voice_id { shift->_execute('voice-id', @_) }
724 0     0 1   sub vpc_lattice { shift->_execute('vpc-lattice', @_) }
725 0     0 1   sub waf { shift->_execute('waf', @_) }
726 0     0 1   sub waf_regional { shift->_execute('waf-regional', @_) }
727 0     0 1   sub wafv2 { shift->_execute('wafv2', @_) }
728 0     0 1   sub wellarchitected { shift->_execute('wellarchitected', @_) }
729 0     0 1   sub wisdom { shift->_execute('wisdom', @_) }
730 0     0 1   sub workdocs { shift->_execute('workdocs', @_) }
731 0     0 1   sub worklink { shift->_execute('worklink', @_) }
732 0     0 1   sub workmail { shift->_execute('workmail', @_) }
733 0     0 1   sub workmailmessageflow { shift->_execute('workmailmessageflow', @_) }
734 0     0 1   sub workspaces { shift->_execute('workspaces', @_) }
735 0     0 1   sub workspaces_web { shift->_execute('workspaces-web', @_) }
736 0     0 1   sub xray { shift->_execute('xray', @_) }
737              
738             1;
739              
740             __END__