File Coverage

blib/lib/Net/Amazon/DirectConnect.pm
Criterion Covered Total %
statement 27 88 30.6
branch 0 38 0.0
condition 0 2 0.0
subroutine 9 18 50.0
pod 6 6 100.0
total 42 152 27.6


line stmt bran cond sub pod time code
1             package Net::Amazon::DirectConnect;
2              
3 1     1   873 use 5.10.0;
  1         3  
  1         41  
4 1     1   5 use strict;
  1         1  
  1         29  
5 1     1   5 use warnings FATAL => 'all';
  1         11  
  1         91  
6              
7 1     1   6 use Carp;
  1         2  
  1         104  
8 1     1   6 use JSON;
  1         2  
  1         6  
9 1     1   723 use YAML::Tiny;
  1         5840  
  1         74  
10 1     1   680 use HTTP::Request;
  1         21717  
  1         52  
11 1     1   713 use LWP::UserAgent;
  1         22842  
  1         59  
12 1     1   721 use Net::Amazon::Signature::V4;
  1         167456  
  1         819  
13              
14             my $yaml = YAML::Tiny->read_string(do { local $/; });
15             close(DATA);
16              
17             =head1 NAME
18              
19             Net::Amazon::DirectConnect - Perl interface to the Amazon DirectConnect API
20              
21             =head1 VERSION
22              
23             Version 0.12
24             DirectConnect API version 2012-10-25
25              
26             =cut
27              
28             our $VERSION = '0.12';
29              
30             =head1 SYNOPSIS
31              
32             use Net::Amazon::DirectConnect;
33              
34             my $dc = Net::Amazon::DirectConnect->new(
35             region => 'ap-southeast-2',
36             access_key_id => 'access key',
37             secret_key_id => 'secret key'
38             );
39             ...
40              
41             =head1 SUBROUTINES/METHODS
42              
43             =head2 new
44              
45             use Net::Amazon::DirectConnect;
46              
47             my $dc = Net::Amazon::DirectConnect->new(
48             region => 'ap-southeast-2',
49             access_key_id => 'access key',
50             secret_key_id => 'secret key'
51             );
52             ...
53              
54             =cut
55              
56             sub new {
57 0     0 1   my $self = bless {}, shift;
58 0 0         return unless @_ % 2 == 0;
59              
60 0           my %args = @_;
61              
62 0           my %defaults = (
63             region => 'us-west-1',
64             access_key_id => $ENV{AWS_ACCESS_KEY_ID},
65             secret_key_id => $ENV{AWS_SECRET_ACCESS_KEY},
66              
67             _ua => LWP::UserAgent->new(agent => __PACKAGE__ . '/' . $VERSION),
68             _yaml => $yaml,
69             );
70              
71 0           foreach (keys %defaults) {
72 0 0         $self->{$_} = exists $args{$_} ? $args{$_} : $defaults{$_};
73             }
74              
75 0           $self->{sig} = Net::Amazon::Signature::V4->new($self->{access_key_id}, $self->{secret_key_id}, $self->{region}, 'directconnect');
76              
77 0           return $self;
78             }
79              
80             =head2 action
81              
82             Perform action against the Amazon Direct Connect API. Actions are validated against an embedded copy of
83             DirectConnect-2012-10-25.yml for correctness before the call is made.
84              
85             # List connections
86             my $connections = $dc->action('DescribeConnections');
87              
88             foreach my $dxcon (@{$connections->{connections}}) {
89             say "$dxcon->{connectionId} -> $dxcon->{connectionName}";
90              
91             # List Virtual Interfaces
92             my $virtual_interfaces = $dc->action('DescribeVirtualInterfaces', connectionId => $dxcon->{connectionId});
93             foreach my $vif (@{$virtual_interfaces->{virtualInterfaces}}) {
94             say " $vif->{connectionId}";
95             }
96             }
97              
98             =cut
99              
100             sub action {
101 0     0 1   my $self = shift;
102 0           my $method = shift;
103 0 0         return unless @_ % 2 == 0;
104 0           my %args = @_;
105              
106 0           $self->_validate($method, \%args);
107              
108 0           my $response = $self->_request($method,
109             content => encode_json \%args
110             );
111              
112 0 0         return decode_json $response->content if $response->is_success;
113             }
114              
115             =head2 ua
116              
117             Get or set UserAgent object
118              
119             say ref($dc->ua);
120             my $ua = my $lwp = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } );
121             $ua->proxy('https', 'http://127.0.0.1:8080');
122             $dc->ua($ua);
123              
124             =cut
125              
126             sub ua {
127 0 0   0 1   ( ref $_[1] ) ? shift->{_ua} = $_[1] : shift->{_ua};
128             }
129              
130             =head2 spec
131              
132             Get or set YAML::Tiny object
133              
134             say ref($dc->spec);
135             $dc->spec(YAML::Tiny->read('new-spec.yml'));
136              
137             =cut
138              
139             sub spec {
140 0 0   0 1   ( ref $_[1] ) ? shift->{_yaml} = $_[1] : shift->{_yaml}->[0];
141             }
142              
143             =head2 region
144              
145             Get or set AWS region
146              
147             $dc->region('ap-southeast-2');
148             say $dc->region;
149              
150             =cut
151              
152             sub region {
153 0     0 1   my $self = shift;
154              
155 0 0         if (exists $_[0]) {
156 0           $self->{region} = shift;
157 0           $self->{sig} = Net::Amazon::Signature::V4->new($self->{access_key_id}, $self->{secret_key_id}, $self->{region}, 'directconnect');
158             }
159              
160 0           return $self->{region};
161             }
162              
163             =head2 credentials
164              
165             Set AWS credentials
166              
167             $dc->credentials(
168             access_key_id => 'MY_ACCESS_KEY',
169             secret_key_id => 'MY_SECRET_KEY'
170             );
171              
172             =cut
173              
174             sub credentials {
175 0     0 1   my $self = shift;
176              
177 0 0         return unless @_ % 2 == 0;
178 0           my %args = @_;
179              
180 0           foreach (qw(access_key_id secret_key_id)) {
181 0           $self->{$_} = $args{$_};
182             }
183              
184 0           return 1;
185             }
186              
187             =head1 Internal subroutines
188              
189             =head2 _request
190              
191             Build and sign HTTP::Request object, return if successful or croak if error
192              
193             =cut
194              
195             sub _request {
196 0     0     my $self = shift;
197 0           my $operation = shift;
198 0 0         return unless @_ % 2 == 0;
199 0           my %args = @_;
200              
201 0 0         croak __PACKAGE__ . '->_request: Missing operation' unless $operation;
202 0 0         croak __PACKAGE__ . '->_request: Invalid or empty region' unless $self->{region};
203              
204 0           my $host = sprintf 'directconnect.%s.amazonaws.com/', $self->{region};
205 0           my $headers = [
206             Version => $self->spec->{api_version},
207             Host => $host,
208             Date => POSIX::strftime( '%Y%m%dT%H%M%SZ', gmtime ),
209             'Content-Type' => 'application/x-amz-json-1.1',
210             'X-Amz-Target' => $self->spec->{target_prefix} . $operation,
211 0 0         exists $args{headers} ? @{$args{headers}} : ()
212             ];
213              
214 0           my $req = HTTP::Request->new(POST => "https://$host", $headers);
215 0 0         $req->content($args{content}) if exists $args{content};
216              
217 0           $req = $self->{sig}->sign($req);
218              
219 0           my $response = $self->ua->request($req);
220 0 0 0       croak __PACKAGE__ . sprintf('->_request: %s %s', decode_json($response->content)->{__type}, decode_json($response->content)->{message} || '') unless $response->is_success;
221              
222 0           return $response;
223             }
224              
225             =head2 _validate
226              
227             Validate the method and required arguments against the current version of the Direct Connect API (2012-10-25)
228              
229             =cut
230              
231             sub _validate {
232 0     0     my $self = shift;
233 0           my $method = shift;
234 0           my $args = shift;
235              
236 0           my ($spec) = grep { $_->{name} eq $method } @{$self->spec->{operations}};
  0            
  0            
237 0 0         return unless ref $spec;
238              
239             local *check_yaml = sub {
240 0     0     my $s = shift;
241 0           my $o = shift;
242              
243 0           foreach (keys %$s) {
244 0 0         if (grep /^required$/, @{$s->{$_}}) {
  0            
245 0 0         croak __PACKAGE__ . ": $method called without required field ($_)" unless exists $o->{$_};
246             }
247              
248 0 0         if (ref $s->{$_}->[0] eq 'HASH') {
249 0 0         return unless check_yaml($s->{$_}->[0]->{structure}, $o->{$_});
250             }
251             }
252              
253 0           return 1;
254 0           };
255              
256 0           return check_yaml($spec->{inputs}, $args);
257             }
258              
259             =head1 AUTHOR
260              
261             Cameron Daniel, C<< >>
262              
263             =head1 SUPPORT
264              
265             You can find documentation for this module with the perldoc command or at https://github.com/megaport/p5-net-amazon-directconnect/
266              
267             perldoc Net::Amazon::DirectConnect
268              
269             =cut
270              
271             1;
272              
273             __DATA__