]>
Commit | Line | Data |
---|---|---|
362a19d8 MG |
1 | package WebService::Scaleway; |
2 | ||
3 | use 5.014000; | |
4 | use strict; | |
5 | use warnings; | |
6 | ||
7 | our $VERSION = '0.001'; | |
8 | ||
9 | use Carp qw/croak/; | |
10 | use HTTP::Tiny; | |
11 | use JSON::MaybeXS; | |
12 | use Scalar::Util qw/blessed/; | |
13 | ||
14 | my $ht = HTTP::Tiny->new( | |
15 | agent => "WebService-Scaleway/$VERSION ", | |
16 | verify_SSL => 1, | |
17 | ); | |
18 | ||
19 | # Instance of WebService::Scaleway with no API key | |
20 | # Used to create tokens from email/password | |
21 | my $dummy = ''; | |
22 | $dummy = bless \$dummy, __PACKAGE__; | |
23 | ||
24 | sub _account ($) { "https://account.scaleway.com$_[0]"} | |
25 | sub _api ($) { "https://api.scaleway.com$_[0]" } | |
26 | ||
27 | sub _request { | |
28 | my ($self, $method, $url, $opts) = @_; | |
29 | $opts->{headers} //= {}; | |
30 | $opts->{headers}{'X-Auth-Token'} = $$self if $$self; | |
31 | $opts->{headers}{'Content-Type'} = 'application/json'; | |
32 | my $ret = $ht->request($method, $url, $opts); | |
33 | die 'Request to Scaleway API server was unsuccessful: ' . $ret->{status} . ' ' . $ret->{reason} . '; ' . $ret->{content} unless $ret->{success}; | |
34 | ||
35 | decode_json $ret->{content} if $ret->{status} != 204; | |
36 | } | |
37 | ||
38 | sub _get { shift->_request(GET => @_) } | |
39 | sub _post { shift->_request(POST => @_) } | |
40 | sub _patch { shift->_request(PATCH => @_) } | |
41 | sub _put { shift->_request(PUT => @_) } | |
42 | sub _delete { shift->_request(DELETE => @_) } | |
43 | ||
44 | sub _tores { | |
45 | my @ret = map { bless $_, 'WebService::Scaleway::Resource' } @_; | |
46 | wantarray ? @ret : $ret[0] | |
47 | } | |
48 | ||
49 | sub new { | |
50 | my ($class, $token) = @_; | |
51 | $token = $dummy->create_token(@_[1..$#_])->id if @_ > 2; | |
52 | ||
53 | bless \$token, $class | |
54 | } | |
55 | ||
56 | BEGIN { | |
57 | my @account_res = qw/token organization user/; | |
58 | my @api_res = qw/server volume snapshot image ip security_group/; | |
59 | ||
60 | my %res = ( | |
61 | map ({ $_ => _account "/${_}s" } @account_res), | |
62 | map { $_ => _api "/${_}s" } @api_res); | |
63 | ||
64 | my %create_parms = ( | |
65 | token => [qw/email password expires/], | |
66 | server => [qw/name organization image volumes tags/], | |
67 | volume => [qw/name organization volume_type size/], | |
68 | snapshot => [qw/name organization volume_id/], | |
69 | image => [qw/name organization root_volume arch/], | |
70 | ip => [qw/ organization/], | |
71 | security_group => [qw/name organization description/], | |
72 | ); | |
73 | ||
74 | sub dynsub { | |
75 | no strict 'refs'; | |
76 | my $sub = pop; | |
77 | *$_ = $sub for @_ | |
78 | } | |
79 | ||
80 | for my $res (keys %res) { | |
81 | dynsub $res, "get_$res", sub { | |
82 | local *__ANON__ = $res; | |
83 | _tores shift->_get("$res{$res}/$_[0]")->{$res} | |
84 | }; | |
85 | ||
86 | dynsub $res.'s', "list_$res".'s', sub { | |
87 | local *__ANON__ = $res.'s'; | |
88 | my @ret = _tores @{shift->_get($res{$res})->{$res.'s'}}; | |
89 | wantarray ? @ret : $ret[0] | |
90 | }; | |
91 | ||
92 | dynsub "delete_$res", sub { | |
93 | local *__ANON__ = "delete_$res"; | |
94 | shift->_delete("$res{$res}/$_[0]") | |
95 | }; | |
96 | ||
97 | dynsub "create_$res", sub { | |
98 | local *__ANON__ = "create_$res"; | |
99 | my $self = shift; | |
100 | my $content = $_[0]; | |
101 | if (blessed $content || ref $content ne 'HASH') { | |
102 | croak "create_$res does not understand positional parameters, pass a hashref instead\n" unless $create_parms{$res}; | |
103 | my @parms = @{$create_parms{$res}}; | |
104 | $content = { map { | |
105 | $parms[$_] => (blessed $_[$_] ? $_[$_]->id : $_[$_]) } 0 .. $#_ }; | |
106 | } | |
107 | _tores $self->_post($res{$res}, { content => encode_json $content })->{$res} | |
108 | }; | |
109 | ||
110 | dynsub "update_$res", sub { | |
111 | local *__ANON__ = "update_$res"; | |
112 | my $data = blessed $_[1] ? {%{$_[1]}} : $_[1]; | |
113 | shift->_put("$res{$res}/".$data->{id}, { content => encode_json $data }) | |
114 | }; | |
115 | } | |
116 | } | |
117 | ||
118 | sub security_group_rule { | |
119 | _tores shift->_get(_api "/security_groups/$_[0]/rules/$_[1]")->{rule} | |
120 | } | |
121 | ||
122 | sub security_group_rules { | |
123 | _tores @{shift->_get(_api "/security_groups/$_[0]/rules")->{rules}} | |
124 | } | |
125 | ||
126 | BEGIN { | |
127 | *get_security_group_rule = \&security_group_rule; | |
128 | *list_security_group_rule = \&security_group_rules; | |
129 | } | |
130 | ||
131 | sub delete_security_group_rule { | |
132 | shift->_delete(_api "/security_groups/$_[0]/rules/$_[1]") | |
133 | } | |
134 | ||
135 | sub create_security_group_rule { | |
136 | my $self = shift; | |
137 | my $grp = shift; | |
138 | my $content = $_[0]; | |
139 | unless (ref $content eq 'HASH') { | |
140 | my @parms = qw/organization action direction ip_range protocol dest_port_from/; | |
141 | $content = { map { $parms[$_] => $_[$_] } 0 .. $#_ }; | |
142 | } | |
143 | $self->_post(_api "/security_groups/$grp/rules", { content => encode_json $content }) | |
144 | } | |
145 | ||
146 | sub update_security_group_rule { | |
147 | my $data = blessed $_[2] ? {%{$_[2]}} : $_[2]; | |
148 | shift->_put (_api "/security_groups/$_[0]/rules/".$data->{id}, { content => encode_json $data }) | |
149 | } | |
150 | ||
151 | sub server_actions { | |
152 | @{shift->_get(_api "/servers/$_[0]/action")->{actions}} | |
153 | } | |
154 | ||
155 | BEGIN { *list_server_actions = \&server_actions } | |
156 | ||
157 | sub perform_server_action { | |
158 | my $content = encode_json { action => $_[1] }; | |
159 | _tores shift->_post(_api "/servers/$_[0]/action", { content => $content })->{task}; | |
160 | } | |
161 | ||
162 | sub refresh_token { | |
163 | _tores shift->_patch(_account "/tokens/$_[0]")->{token} | |
164 | } | |
165 | ||
166 | sub server_metadata { | |
167 | _tores $dummy->_get('http://169.254.42.42/conf?format=json') | |
168 | } | |
169 | ||
170 | package # hide from PAUSE | |
171 | WebService::Scaleway::Resource; | |
172 | ||
173 | use overload '""' => sub { shift->id }; | |
174 | ||
175 | our $AUTOLOAD; | |
176 | sub AUTOLOAD { | |
177 | my ($self) = @_; | |
178 | my ($attr) = $AUTOLOAD =~ m/::([^:]*)$/s; | |
179 | die "No such attribute: $attr" unless exists $self->{$attr}; | |
180 | $self->{$attr} | |
181 | } | |
182 | ||
183 | sub can { | |
184 | my ($self, $sub) = @_; | |
185 | exists $self->{$sub} ? sub { shift->{$sub} } : undef | |
186 | } | |
187 | ||
188 | sub DESTROY {} # Don't call AUTOLOAD on destruction | |
189 | ||
190 | 1; | |
191 | __END__ | |
192 | ||
193 | =encoding utf-8 | |
194 | ||
195 | =head1 NAME | |
196 | ||
197 | WebService::Scaleway - Perl interface to Scaleway cloud server provider API | |
198 | ||
199 | =head1 SYNOPSIS | |
200 | ||
201 | use WebService::Scaleway; | |
202 | my $token = ...; # API token here | |
203 | my $sw = WebService::Scaleway->new($token); | |
204 | my $org = $sw->organizations; | |
205 | ||
206 | # Create an IP, a volume, and use them for a new Debian Jessie server | |
207 | my $ip = $sw->create_ip($org); | |
208 | my $vol = $sw->create_volume('testvol', $org, 'l_ssd', 50_000_000_000); | |
209 | my ($debian) = grep { $_->name =~ /debian jessie/i } $sw->images; | |
210 | my $srv = $sw->create_server('testsrv', $org, $debian, {1 => {%$vol}}); | |
211 | ||
212 | # Change the server name | |
213 | $srv->{name} = 'Debian'; | |
214 | $sw->update_server($srv); | |
215 | ||
216 | # Boot the server | |
217 | $sw->perform_server_action($srv, 'poweron'); | |
218 | say "The server is now booting. To access it, do ssh root@", $ip->address; | |
219 | ||
220 | =head1 DESCRIPTION | |
221 | ||
222 | Scaleway is an IaaS provider that offers bare metal ARM cloud servers. | |
223 | WebService::Scaleway is a Perl interface to the Scaleway API. | |
224 | ||
225 | =head2 Constructors | |
226 | ||
227 | WebService::Scaleway objects are defined by their authentication | |
228 | token. There are two consructors: | |
229 | ||
230 | =over | |
231 | ||
232 | =item WebService::Scaleway->B<new>(I<$auth_token>) | |
233 | ||
234 | Construct a WebService::Scaleway object from a given authentication | |
235 | token. | |
236 | ||
237 | =item WebService::Scaleway->B<new>(I<$email>, I<$password>) | |
238 | ||
239 | Construct a WebService::Scaleway object from an authentication token | |
240 | obtained by logging in with the given credentials. | |
241 | ||
242 | =back | |
243 | ||
244 | =head2 Listing resources | |
245 | ||
246 | These methods return a list of all resources of a given type | |
247 | associated to your account. Each resource is a blessed hashref with | |
248 | C<AUTOLOAD>ed accessors (for example C<< $resource->{name} >> can be | |
249 | written as C<< $resource->name >>) and that stringifies to the ID of | |
250 | the resource: C<< $resource->id >>. | |
251 | ||
252 | There is no difference between B<resources>() and | |
253 | B<list_resources>(). | |
254 | ||
255 | =over | |
256 | ||
257 | =item $self->B<tokens> | |
258 | ||
259 | =item $self->B<list_tokens> | |
260 | ||
261 | Official documentation: L<https://developer.scaleway.com/#tokens-tokens-get>. | |
262 | ||
263 | =item $self->B<organizations> | |
264 | ||
265 | =item $self->B<list_organizations> | |
266 | ||
267 | Official documentation: L<https://developer.scaleway.com/#organizations-organizations>. | |
268 | ||
269 | =item $self->B<servers> | |
270 | ||
271 | =item $self->B<list_servers> | |
272 | ||
273 | Official documentation: L<https://developer.scaleway.com/#servers-servers-get>. | |
274 | ||
275 | =item $self->B<volumes> | |
276 | ||
277 | =item $self->B<list_volumes> | |
278 | ||
279 | Official documentation: L<https://developer.scaleway.com/#volumes-volumes-get>. | |
280 | ||
281 | =item $self->B<snapshots> | |
282 | ||
283 | =item $self->B<list_snapshots> | |
284 | ||
285 | Official documentation: L<https://developer.scaleway.com/#snapshots-snapshots-get>. | |
286 | ||
287 | =item $self->B<images> | |
288 | ||
289 | =item $self->B<list_images> | |
290 | ||
291 | Official documentation: L<https://developer.scaleway.com/#images-images-get>. | |
292 | ||
293 | =item $self->B<ips> | |
294 | ||
295 | =item $self->B<list_ips> | |
296 | ||
297 | Official documentation: L<https://developer.scaleway.com/#ips-ips-get>. | |
298 | ||
299 | =item $self->B<security_groups> | |
300 | ||
301 | =item $self->B<list_security_groups> | |
302 | ||
303 | Official documentation: L<https://developer.scaleway.com/#security-groups-security-groups-get>. | |
304 | ||
305 | =item $self->B<security_group_rules>(I<$group_id>) | |
306 | ||
307 | =item $self->B<list_security_group_rules>(I<$group_id>) | |
308 | ||
309 | Official documentation: L<https://developer.scaleway.com/#security-groups-manage-rules-get>. | |
310 | ||
311 | =back | |
312 | ||
313 | =head2 Retrieving resources | |
314 | ||
315 | These methods take the id of a resource and return the resource as a | |
316 | blessed hashref as described in the previous section. | |
317 | ||
318 | There is no difference between B<resource>(I<$id>) and | |
319 | B<get_resource>(I<$id>). | |
320 | ||
321 | =over | |
322 | ||
323 | =item $self->B<token>(I<$id>) | |
324 | ||
325 | =item $self->B<get_token>(I<$id>) | |
326 | ||
327 | Official documentation: L<https://developer.scaleway.com/#tokens-token-get>. | |
328 | ||
329 | =item $self->B<user>(I<$id>) | |
330 | ||
331 | =item $self->B<get_user>(I<$id>) | |
332 | ||
333 | Official documentation: L<https://developer.scaleway.com/#users-user>. | |
334 | ||
335 | =item $self->B<server>(I<$id>) | |
336 | ||
337 | =item $self->B<get_server>(I<$id>) | |
338 | ||
339 | Official documentation: L<https://developer.scaleway.com/#servers-server-get>. | |
340 | ||
341 | =item $self->B<volume>(I<$id>) | |
342 | ||
343 | =item $self->B<get_volume>(I<$id>) | |
344 | ||
345 | Official documentation: L<https://developer.scaleway.com/#volumes-volume-get>. | |
346 | ||
347 | =item $self->B<snapshot>(I<$id>) | |
348 | ||
349 | =item $self->B<get_snapshot>(I<$id>) | |
350 | ||
351 | Official documentation: L<https://developer.scaleway.com/#snapshots-snapshot-get>. | |
352 | ||
353 | =item $self->B<image>(I<$id>) | |
354 | ||
355 | =item $self->B<get_image>(I<$id>) | |
356 | ||
357 | Official documentation: L<https://developer.scaleway.com/#images-operation-on-a-single-image-get>. | |
358 | ||
359 | =item $self->B<ip>(I<$id>) | |
360 | ||
361 | =item $self->B<get_ip>(I<$id>) | |
362 | ||
363 | Official documentation: L<https://developer.scaleway.com/#ips-ip-get>. | |
364 | ||
365 | =item $self->B<security_group>(I<$id>) | |
366 | ||
367 | =item $self->B<get_security_group>(I<$id>) | |
368 | ||
369 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-groups-get>. | |
370 | ||
371 | =item $self->B<security_group_rule>(I<$group_id>, I<$rule_id>) | |
372 | ||
373 | =item $self->B<get_security_group_rule>(I<$group_id>, I<$rule_id>) | |
374 | ||
375 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-rule-get>. | |
376 | ||
377 | =back | |
378 | ||
379 | =head2 Deleting resources | |
380 | ||
381 | These methods take the id of a resource and delete it. They do not | |
382 | return anything. | |
383 | ||
384 | =over | |
385 | ||
386 | =item $self->B<delete_token>(I<$id>) | |
387 | ||
388 | Official documentation: L<https://developer.scaleway.com/#tokens-token-delete>. | |
389 | ||
390 | =item $self->B<delete_server>(I<$id>) | |
391 | ||
392 | Official documentation: L<https://developer.scaleway.com/#servers-server-delete>. | |
393 | ||
394 | =item $self->B<delete_volume>(I<$id>) | |
395 | ||
396 | Official documentation: L<https://developer.scaleway.com/#volumes-volume-delete>. | |
397 | ||
398 | =item $self->B<delete_snapshot>(I<$id>) | |
399 | ||
400 | Official documentation: L<https://developer.scaleway.com/#snapshots-snapshot-delete>. | |
401 | ||
402 | =item $self->B<delete_image>(I<$id>) | |
403 | ||
404 | Official documentation: L<https://developer.scaleway.com/#images-operation-on-a-single-image-delete>. | |
405 | ||
406 | =item $self->B<delete_ip>(I<$id>) | |
407 | ||
408 | Official documentation: L<https://developer.scaleway.com/#ips-ip-delete>. | |
409 | ||
410 | =item $self->B<delete_security_group>(I<$id>) | |
411 | ||
412 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-groups-delete>. | |
413 | ||
414 | =item $self->B<delete_security_group_rule>(I<$group_id>, I<$rule_id>) | |
415 | ||
416 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-rule-delete>. | |
417 | ||
418 | =back | |
419 | ||
420 | =head2 Modifying resources | |
421 | ||
422 | These methods take a hashref representing a resource that already | |
423 | exists and update it. The value of C<< $resource->{id} >> is used for | |
424 | identifying this resource on the remote end. Both blessed and | |
425 | unblessed hashrefs are accepted. The updated resource is returned as a | |
426 | blessed hashref as described in L</"Listing resources">. | |
427 | ||
428 | =over | |
429 | ||
430 | =item $self->B<update_server>(I<$resource>) | |
431 | ||
432 | Official documentation: L<https://developer.scaleway.com/#servers-server-put>. | |
433 | ||
434 | =item $self->B<update_snapshot>(I<$resource>) | |
435 | ||
436 | Official documentation: L<https://developer.scaleway.com/#snapshots-snapshot-put>. | |
437 | ||
438 | =item $self->B<update_image>(I<$resource>) | |
439 | ||
440 | Official documentation: L<https://developer.scaleway.com/#images-operation-on-a-single-image-put>. | |
441 | ||
442 | =item $self->B<update_ip>(I<$resource>) | |
443 | ||
444 | Official documentation: L<https://developer.scaleway.com/#ips-ip-put>. | |
445 | ||
446 | =item $self->B<update_security_group>(I<$resource>) | |
447 | ||
448 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-groups-put>. | |
449 | ||
450 | =item $self->B<update_security_group_rule>(I<$group_id>, I<$resource>) | |
451 | ||
452 | Official documentation: L<https://developer.scaleway.com/#security-groups-operation-on-a-security-rule-put>. | |
453 | ||
454 | =back | |
455 | ||
456 | =head2 Creating resources | |
457 | ||
458 | These methods take either a hash that is passed directly to the API or | |
459 | a method-specific list of positional parameters. They create a new | |
460 | resource and return it as a blessed hashref as described in | |
461 | L</"Listing resources">. | |
462 | ||
463 | When using positional parameters, you can pass a resource in blessed | |
464 | hashref format where a resource ID is expected. The function will call | |
465 | C<< ->id >> on the resouce automatically. | |
466 | ||
467 | Most of these methods require an organization ID. You can obtain it | |
468 | with the B<organizations> method described above. | |
469 | ||
470 | =over | |
471 | ||
472 | =item $self->B<create_token>(I<\%data>) | |
473 | ||
474 | =item $self->B<create_token>(I<$email>, I<$password>, [I<$expires>]) | |
475 | ||
476 | Authenticates a user against their username and password and returns | |
477 | an authentication token. If I<$expires> (default: false) is true, the | |
478 | token will expire. | |
479 | ||
480 | This method is called internally by the two-argument constructor. | |
481 | ||
482 | Official documentation: L<https://developer.scaleway.com/#tokens-tokens-get>. | |
483 | ||
484 | =item $self->B<create_server>(I<\%data>) | |
485 | ||
486 | =item $self->B<create_server>(I<$name>, I<$organization>, I<$image>, I<$volumes>, [I<$tags>]) | |
487 | ||
488 | Creates and returns a new server. | |
489 | ||
490 | I<$name> is the server name. I<$organization> is the organization ID. | |
491 | I<$image> is the image ID. I<$volumes> is a "sparse array" (hashref | |
492 | from indexes to volumes, indexed from 1) of volumes. I<$tags> is an | |
493 | optional arrayref of tags. | |
494 | ||
495 | Note that the volumes must be unblessed hashrefs. If I<$vol> is a | |
496 | volume, you can use this idiom: C<< $volumes = {1 => {%$vol}} >>. | |
497 | ||
498 | Official documentation: L<https://developer.scaleway.com/#servers-servers-get>. | |
499 | ||
500 | =item $self->B<create_volume>(I<\%data>) | |
501 | ||
502 | =item $self->B<create_volume>(I<$name>, I<$organization>, I<$volume_type>, I<$size>) | |
503 | ||
504 | Creates and returns a new volume. I<$volume_type> currently must be | |
505 | C<l_ssd>. I<$size> is the size in bytes. | |
506 | ||
507 | Official documentation: L<https://developer.scaleway.com/#volumes-volumes-get>. | |
508 | ||
509 | =item $self->B<create_snapshot>(I<\%data>) | |
510 | ||
511 | =item $self->B<create_snapshot>(I<$name>, I<$organization>, I<$volume_id>) | |
512 | ||
513 | Creates and returns a snapshot of the volume I<$volume_id>. | |
514 | ||
515 | Official documentation: L<https://developer.scaleway.com/#snapshots-snapshots-get>. | |
516 | ||
517 | =item $self->B<create_image>(I<\%data>) | |
518 | ||
519 | =item $self->B<create_image>(I<$name>, I<$organization>, I<$root_volume>, I<$arch>) | |
520 | ||
521 | Creates and returns an image from the volume I<$root_volume>. I<$arch> | |
522 | is the architecture of the image (currently must be C<"arm">). | |
523 | ||
524 | Official documentation: L<https://developer.scaleway.com/#images-images-get>. | |
525 | ||
526 | =item $self->B<create_ip>(I<\%data>) | |
527 | ||
528 | =item $self->B<create_ip>(I<$organization>) | |
529 | ||
530 | Official documentation: L<https://developer.scaleway.com/#ips-ips-get>. | |
531 | ||
532 | =item $self->B<create_security_group>(I<\%data>) | |
533 | ||
534 | =item $self->B<create_security_group>(I<$name>, I<$organization>, I<$description>) | |
535 | ||
536 | Official documentation: L<https://developer.scaleway.com/#security-groups-security-groups-get>. | |
537 | ||
538 | =item $self->B<create_security_group_rule>(I<$group_id>) | |
539 | ||
540 | =item $self->B<create_security_group_rule>(I<$group_id>, I<$organization>, I<$action>, I<$direction>, I<$ip_range>, I<$protocol>, [<$dest_port_from>]) | |
541 | ||
542 | ||
543 | Official documentation: L<https://developer.scaleway.com/#security-groups-manage-rules-get>. | |
544 | ||
545 | =back | |
546 | ||
547 | =head2 Miscellaneous methods | |
548 | ||
549 | These are methods that don't fit any previous category. Any use of | |
550 | "blessed hashref" refers to the concept described in L</"Listing | |
551 | resources">. Wherever a resource ID is expected, you can instead pass | |
552 | a resource as a blessed hashref and the method will call C<< ->id >> | |
553 | on it for you. | |
554 | ||
555 | =over | |
556 | ||
557 | =item $self->B<server_actions>(I<$server_id>) | |
558 | ||
559 | =item $self->B<list_server_actions>(I<$server_id>) | |
560 | ||
561 | Returns a list of strings representing possible actions you can | |
562 | perform on the given server. Example actions are powering on/off a | |
563 | server or rebooting it. | |
564 | ||
565 | Official documentation: L<https://developer.scaleway.com/#servers-actions-get> | |
566 | ||
567 | =item $self->B<perform_server_action>(I<$server_id>, I<$action>) | |
568 | ||
569 | Performs an action on a server. I<$action> is one of the strings | |
570 | returned by B<server_actions>. The function returns a blessed hashref | |
571 | with information about the task. | |
572 | ||
573 | This is not very useful, as this module does not currently offer any | |
574 | function for tracking tasks. | |
575 | ||
576 | Official documentation: L<https://developer.scaleway.com/#servers-actions-post> | |
577 | ||
578 | =item $self->B<refresh_token>(I<$token_id>) | |
579 | ||
580 | This method takes the ID of an expirable token, extends its expiration | |
581 | date by 30 minutes, and returns the new token as a blessed hashref. | |
582 | ||
583 | Official documentation: L<https://developer.scaleway.com/#tokens-token-patch> | |
584 | ||
585 | =item $self->B<server_metadata> | |
586 | ||
587 | This method can only be called from a Scaleway server. It returns | |
588 | information about the server as a blessed hashref. | |
589 | ||
590 | Official documentation: L<https://developer.scaleway.com/#metadata-c1-server-metadata> | |
591 | ||
592 | =back | |
593 | ||
594 | =head1 SEE ALSO | |
595 | ||
596 | L<https://developer.scaleway.com/> | |
597 | ||
598 | =head1 AUTHOR | |
599 | ||
600 | Marius Gavrilescu, E<lt>marius@ieval.roE<gt> | |
601 | ||
602 | =head1 COPYRIGHT AND LICENSE | |
603 | ||
604 | Copyright (C) 2015 by Marius Gavrilescu | |
605 | ||
606 | This library is free software; you can redistribute it and/or modify | |
607 | it under the same terms as Perl itself, either Perl version 5.20.2 or, | |
608 | at your option, any later version of Perl 5 you may have available. | |
609 | ||
610 | ||
611 | =cut |