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 variable DIGITALOCEAN_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 or undef, then the environment variable DIGITALOCEAN_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 and tags 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 a names 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

  • Github

  • Topic Map knowledge in ontologies/digitalocean-clients.atm in this distribution

  • DigitalOcean API

  • 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.