NAME
Net::Async::DigitalOcean - Async client for DigitalOcean REST APIv2
SYNOPSIS
use IO::Async::Loop;
my $loop = IO::Async::Loop->new; # the god-like event loop
use Net::Async::DigitalOcean;
my $do = Net::Async::DigitalOcean->new( loop => $loop );
$do->start_actionables; # activate polling incomplete actions
# create a domain, wait for it
$do->create_domain( {name => "example.com"} )
->get; # block here
# create a droplet, wait for it
my $dr = $do->create_droplet({
"name" => "www.example.com",
"region" => "nyc3",
"size" => "s-1vcpu-1gb",
"image" => "openfaas-18-04",
"ssh_keys" => [],
"backups" => 'true',
"ipv6" => 'true',
"monitoring" => 'true',
})
->get; $dr = $dr->{droplet}; # skip type
# reboot
$do->reboot(id => $dr->{id})->get;
# reboot all droplets tagged with 'prod:web'
$do->reboot(tag => 'prod:web')->get;
OVERVIEW
Platform
DigitalOcean is a cloud provider which offers you to spin up servers (droplets) with a specified OS, predefined sizes in predefined regions. You can also procure storage volumes, attach those to the droplets, make snapshots of the volumes or the whole droplet. There are also interfaces to create and manage domains and domain record, ssh keys, various kinds of images or tags to tag the above things. On top of that you can build systems with load balancers, firewalls, distributable objects (Spaces, similar to Amazon's S3). Or, you can go along with the Docker pathway and/or create and run kubernetes structures.
See the DigitalOcean Platform for more.
DigitalOcean offers a web console to administrate all this, but also a RESTy interface (and Terraform for that matter)
REST API, asynchronous
This client library can be used by applications to talk to the various DigitalOcean REST endpoints. But in contrast to similar libraries, such as DigitalOcean or WebService::DigitalOcean, this library operates in asynchronous mode:
Firstly, all HTTP requests are launched asynchronously, without blocking until their respective responses come in.
But more importantly, long-lasting actions, such as creating a droplet, snapshoting volumes or rebooting a set of droplets are handled by the library itself; the application does not need to keep track of these open actions, or keep polling for their completion.
The way this works is that the application first has to create the event loop and - with it - create a handle to the DigitalOcean API server:
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
use Net::Async::DigitalOcean;
my $do = Net::Async::DigitalOcean->new( loop => $loop );
$do->start_actionables;
You also should start a timer actionables. In regular intervals it will check with the server, whether open actions have been completed or not.
With that, every method (except a few) return a Future object, such when creating a droplet:
my $f = $do->create_droplet({
"name" => "example.com",
"region" => "nyc3",
"size" => "s-1vcpu-1gb",
"image" => "openfaas-18-04",
....
});
The application can either choose to wait synchronously:
my $d = $f->get; # wait, and receive the response as HASH
or, alternatively, can specify what should happen once the result comes in:
$f->on_done( sub { my $d = shift;
warn "droplet $d->{droplet}->{name} ready (well, almost)"; } );
Futures can also be combined in various ways; one extremely useful is to wait for several actions to complete in one go:
Future->wait_all(
map { $do->create_volume( ... ) }
qw(one two another) )->get;
Success and Failure
When futures succeed, the application will usually get a result in form of a Perl HASH (see below). If a future fails and has been configured to have a ->on_fail
handler, then that will be invoked. Otherwise an exception will be raised. The library tries to figure out what the real message from the server was.
Data Structures
Another difference to other libraries in this arena is that it does not try to artifically objectify things into classes, such as for the droplet, image and other concepts.
Instead, the library truthfully transports Perl HASHes and LISTs via JSON to the server and back; even to the point to exactly reflect the API specification . That way you can always look up what to precisely expect as result.
But as the server chooses to type results, the application will have to cope with that
my $d = $do->create_droplet({
"name" => "example.com",
....
})->get;
$d = $d->{droplet}; # now I have the droplet itself
Caveat Rate-Limiting
To avoid being swamped the DigitalOcean server enforces several measures to limit abuse:
Limit on the number of HTTP requests within a certain time window.
In the current version this client is rather aggressively trying to get things done. If you get too many TOO_MANY_REQUESTS errors, you may want to increase the poll time of actions (see
actionables
).Future version will support policies to be set by the application.
Limit on the total number of droplets to be created
Such a case will result in an exception.
Limit on the number of droplets to be created in one go
Such a case will result in an exception.
Limit in the number of snapshots
In that case the client will wait for the indicated time. That may well be several minutes!
Limit in the size of volumes
Such a case will result in an exception.
Limit in the size of droplets
Such a case will result in an exception.
INTERFACE
There is only one object class here, that of the DigitalOcean handle. All its methods - unless specifically mentioned - typically return one Future object.
Constants
DIGITALOCEAN_API (string)
Base HTTP endpoint for the DigitalOcean APIv2
Constructor
Following fields are honored:
loop
(required; IO::Async::Loop)Event loop to keep things going.
endpoint
(optional; string)If this field is completely omitted, then the DigitalOcean endpoint is chosen as default.
If the field exists, but is kept
undef
, then the environment variableDIGITALOCEAN_API
is consulted. If that is missing, then an exception is raised.If the field exists, and the value is defined, it will be used.
bearer
(optional; string)To be authenticated to the official DigitalOcean endpoints the library will have to send an
Authentication
HTTP header with the bearer information to the server. Once you have an account, you can create such a bearer token.If this
bearer
field is missing orundef
, then the environment variableDIGITALOCEAN_BEARER
will be consulted. If there is no such token, and the endpoint is the official one, an exception will be raised. Otherwise, the missing bearer is tolerated (as you would if you test against a local server).throtteling
(optional; string)This is currently not implemented.
tracing
(optional; any value)If set to something non-zero, then a HTTP trace (sending and receiving, headers and body) is written to
STDERR
. This helps tremendously during debugging.rate_limit_frequency
(optional; integer; in seconds; default 5)This time interval is used to regularily poll the server for incomplete actions. Note, that for that to happen, you have to start/stop the timer explicitly:
$do->start_actionables; # from now on do something with DigitalOcean $do->stop_actionables; # dont need it anymore
Methods
Polling the Server
start_actionables
([$interval
])This starts the timer. The optional interval integer overrides what the
$do
object would use as default.stop_actionables
Simply stops the timer. At any time it can be restarted.
Meta Interface
If you work with the official DigitalOcean server, then this section can/should be ignored.
This subinterface allows to communicate with test servers to better control the test environent.
meta_reset
This deletes ALL resources on the server, providing a clean slate for a following test.
meta_ping
This pings the server which simply sends a pong response.
meta_account
($account_HASH
)Typically sets/resets operational limits, such as the number of volumes or droplets to be created. This will be more detailed later.
meta_statistics
Returns eventually a rough statistics on what happened on the server.
meta_capabilities
Lists which sections (chapters) of the API specification are implemented on the server. Returns a HASH, to be detailed later.
Account
account
Returns account information for the current user (as identified by the bearer token) as a HASH.
Block Storage
volumes
List all volumes.
volumes
(name =>$name
)List all volumes with a certain name.
create_volume
($volume_HASH
)Instigate to create a volume with your spec.
volume
(id =>$volume_id
)volume
(name =>$name
,$region
)Returns volume information, the volume either identified by its id, or the name/region combination.
snapshots
(volume =>$volume_id
)List volume snapshots.
create_snapshot
($volume_id
,$HASH
)Creates a new volume snapshot with
name
andtags
provided in the HASH.delete_volume
(id =>$volume_id
)delete_volume
(name =>$name
,$region
)Delete a volume, either identified by its id, or the name/region combination.
delete_snapshot
($snapshot_id
)Delete volume snapshot with a given id.
Block Storage Actions
volume_attach
($volume_id
,$attach_HASH
)Attaches a given volume to a droplet specified in the HASH.
Attaching by name is NOT IMPLEMENTED.
Note that the region of the droplet and that of the volume must agree to make that work.
volume_detach
($volume_id
,$attach_HASH
)Detach the specified volume from the droplet named in the HASH.
Detaching by name is NOT IMPLEMENTED.
volume_resize
($volume_id
,$resize_HASH
)Resizes the volume.
Domains
domains
Lists all domains.
create_domain
($domain_HASH
)Creates a domain entry with the given specification.
Note that you can enter here anything, as the DigitialOcean DNS servers are not necessarily authoritative for such a domain.
domain
($name
)Retrieves information of a named domain.
delete_domain
($name
)Deletes the named domain.
Domain Records
domain_records
domain_records
($name
, type =>$record_type
)domain_records
($name
, name =>$record_name
)List domain records of the named domain; either all of them or filtered according to type or to name.
create_record
($name
,$record_HASH
)Create new domain record within the named domain.
domain_record
($name
,$record_id
)Retrieves the record for a given id from the named domain.
update_record
($name
,$record_id
,$record_HASH
)Selectively updates information in the record hash into the domain record with that id, all for the named domain.
delete_record
($name
,$record_id
)Deletes the record with the given id from the named domain.
Droplets
create_droplet
($droplet_HASH
)Instigate to create new droplet(s) specified by the HASH.
If you specify not a
name
field, but anames
field with an ARRAY of names, then multiple droplets will be created. (There is a user-specific limit on how many can be created in one go.)Note that resulting droplets may have the networking information incomplete (as that seems to be determined rather late). To get this right, you will have to retrieve that droplet information a bit later.
droplet
(id =>$droplet_id
)droplet
(name =>$droplet_name
,$region
)Retrieve droplet information based on its id, or alternatively by name and region.
droplets
List all droplets.
Listing of droplets based on name is NOT IMPLEMENTED.
droplets_all
This convenience method will return a future which - when done - will return the complete list of droplets, not just the first page.
droplets_kernels
NOT IMPLEMENTED
snapshots
(droplet =>$droplet_id
)List all droplet snapshots for that very droplet.
backups
($droplet_id
)List backups of droplet specified by id.
droplet_actions
(id =>$droplet_id
)droplet_actions
(tag =>$tag
)NOT IMPLEMENTED
List all actions (also completed ones) of a specific droplet.
delete_droplet
(id =>$droplet_id
)delete_droplet
(tag =>$tag
)Delete a specific droplet by id, or alternatively, a set specified by a tag.
list_neighbors
NOT IMPLEMENTED
associated_resources
(id =>$droplet_id
)List volumes attached, snapshots thereof, and snapshots of the droplet itself.
delete_selective_associated_resources
NOT IMPLEMENTED
delete_with_associated_resources
(id =>$droplet_id
)Deletes the droplet and all its associated resources.
associated_resources
(check_status =>$droplet_id
)Check which resources are already deleted.
delete_with_associated_resources_retry
NOT IMPLEMENTED
Droplet Actions
enable_backups
(id =>$droplet_id
)enable_backups
(tag =>$tag
)Enable regular backups (done by DigitalOcean).
disable_backups
(id =>$droplet_id
)disable_backups
(tag =>$tag
)Disable regular backups.
reboot
(id =>$droplet_id
)reboot
(tag =>$tag
)Reboots the specified droplet(s), either one via the id, or several via a tag.
power_cycle
(id =>$droplet_id
)power_cycle
(tag =>$tag
)Power-cycles the specified droplet(s), either one via the id, or several via a tag.
shutdown
(id =>$droplet_id
)shutdown
(tag =>$tag
)Shuts down the specified droplet(s), either one via the id, or several via a tag.
power_off
(id =>$droplet_id
)power_off
(tag =>$tag
)Powers down the specified droplet(s), either one via the id, or several via a tag.
power_on
(id =>$droplet_id
)power_on
(tag =>$tag
)Powers on the specified droplet(s), either one via the id, or several via a tag.
restore
(id =>$droplet_id
,$image
)restore
(tag =>$tag
,$image
)Restores the specified droplet(s) with the image given.
password_reset
(id =>$droplet_id
)password_reset
(tag =>$tag
)Resets password on the specified droplet(s), either one via the id, or several via a tag.
resize
(id =>$droplet_id
,$new_size
,$diskresize_yes
)resize
(tag =>$tag
,$new_size
,$diskresize_yes
)Resizes the specified droplet(s).
rebuild
(id =>$droplet_id
,$image
)rebuild
(tag =>$tag
,$image
)Rebuilds the specified droplet(s) with the image given.
NOTE: I do not understand the difference to
restore
.rename
(id =>$droplet_id
,$name
)Renames the specified droplet to a new name.
enable_ipv6
(id =>$droplet_id
)enable_ipv6
(tag =>$tag
)Turn on IPv6 on specified droplet(s).
Note, that it takes a while on the server to get this configured.
Note, that there does not seem a way to disable IPv6 for a droplet.
enable_private_networking
(id =>$droplet_id
)enable_private_networking
(tag =>$tag
)Enables ... well.
create_droplet_snapshot
(id =>$droplet_id
)create_droplet_snapshot
(tag =>$tag
)Creates a new snapshot of the specified droplet(s).
droplet_action
NOT IMPLEMENTED
Images
images
List all images.
images
(type => 'distribution')List all distribution images.
images
(type => 'application')List all application images.
images
(private => 'true')List all user images.
images
(tag_name =>$tag
)List all images tagged with the tag.
images_all
This convenience method returns a future, which - when done - will return complete list of images. For that it will iterate over all pages, if any, and collects all results into a list.
create_custom_image
NOT IMPLEMENTED
image
NOT IMPLEMENTED
update_image
NOT IMPLEMENTED
image_actions
NOT IMPLEMENTED
delete_image
NOT IMPLEMENTED
Regions
regions
List all available regions.
Sizes.
sizes
List all sizes.
SSH keys
keys
List all keys.
create_key
($key_HASH
)Create a new key with a provided HASH.
key
($key_id
)Retrieve existing key given by the id.
update_key
($key_id
,$key_HASH
)Selectively update fields for a given key.
delete_key
($key_id
)Delete a specific key.
SEE ALSO
INSTALLATION file in this distribution
examples/*.pl in this distribution
t/*.t test suites in this distribution
Topic Map knowledge in ontologies/digitalocean-clients.atm in this distribution
Other Perl packages which talk to DigitalOcean are DigitalOcean and WebService::DigitalOcean
AUTHOR
Robert Barta, <rho at devc.at>
LICENSE AND COPYRIGHT
Copyright 2021 Robert Barta.
This program is free software; you can redistribute it and/or modify it under the terms of the the Artistic License (2.0). You may obtain a copy of the full license at:
http://www.perlfoundation.org/artistic_license_2_0
Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license.
If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license.
This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder.
This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed.
Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.