Tutorial

How to Use and Understand Action Objects and the DigitalOcean API

Published on February 26, 2015
How to Use and Understand Action Objects and the DigitalOcean API

Introduction

In version two of the DigitalOcean API, each event that occurs creates an “Action” object. These serve both as records of events that have occurred in the past and as a way to check the progress of an on-going event. From creating a new Droplet to transferring an image to a new region, an Action object will provide you with useful information about the event.

This article will explain Action objects and show how they can be used in practice via DropletKit, the official Ruby gem for the DigitalOcean API.

Prerequisites

This article assumes a basic understanding of the DigitalOcean API. To learn more about the API including how to obtain an access token which will be needed to complete this tutorial, see the following resources:

Understanding Action Objects

When initiating an event with the API, an Action object will be returned in the response. This object will contain information about the event including its status, the timestamps for when it was started and completed, and the associated resource type and ID. For instance if we where to take a snapshot of the Droplet with the ID 3164450:

curl -X POST -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer '$TOKEN'' \
    -d '{"type":"snapshot","name":"Nifty New Snapshot"}' \
    "https://api.digitalocean.com/v2/droplets/3164450/actions" 

we would receive this in response:

{
  "action": {
    "id": 36805022,
    "status": "in-progress",
    "type": "snapshot",
    "started_at": "2014-11-14T16:34:39Z",
    "completed_at": null,
    "resource_id": 3164450,
    "resource_type": "droplet",
    "region": "nyc3"
  }
}

Note that the resource_type is droplet and the resource_id is the ID of the Droplet. The status is in-progress. This will change to completed once the event is finished. In order to check on the status of an Action, you can query the API for that Action directly.

curl -X GET -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer '$TOKEN'' \
    "https://api.digitalocean.com/v2/actions/36805022" 

This will return the requested action object:

{
  "action": {
    "id": 36805022,
    "status": "completed",
    "type": "snapshot",
    "started_at": "2014-11-14T16:34:39Z",
    "completed_at": "2014-11-14T16:38:52Z",
    "resource_id": 3164450,
    "resource_type": "droplet",
    "region": "nyc3"
  }
}

Notice how now that the status is completed, there is a timestamp for completed_at as well as started_at.

You can also access a complete history of all Actions taken on your account at the /actions endpoint.

curl -X GET -H 'Content-Type: application/json' \
    -H 'Authorization: Bearer '$TOKEN'' \
    "https://api.digitalocean.com/v2/actions"

Using Actions Objects in Practice

While listing all Action objects may be interesting in order to audit your history, in practice you will mostly use this endpoint in order check on the status of a process. We’ll be using droplet_kit, the official Ruby gem for the DigitalOcean API, for these examples. It can be installed with:

gem install droplet_kit

To get started, enter the Ruby shell by running the command irb Then import the droplet_kit gem and set up your client using your API token:

irb(main):> require 'droplet_kit'
 => true 
irb(main):> client = DropletKit::Client.new(access_token: DO_TOKEN)

Some actions are dependent on others being taken first. For instance, attempting to take a snapshot of a Droplet which is still powered on will lead to an error. A Droplet must be powered off in order to take a snapshot.

irb(main):> client.droplet_actions.snapshot(droplet_id: 4143310, name: 'Snapshot Name')
=> "{\"id\":\"unprocessable_entity\",\"message\":\"Droplet is currently on. Please power it off to run this event.\"}"

Attempting to take a snapshot immediately after initiating a shutdown action will also lead to that same error as you must ensure that the shutdown Action has completed before the snapshot can be taken. Actions can not be queued.

irb(main):> client.droplet_actions.shutdown(droplet_id: 4143310)
=> <DropletKit::Action {:@id=>43918785, :@status=>"in-progress", :@type=>"shutdown", :@started_at=>"2015-02-16T21:22:35Z", :@completed_at=>nil, :@resource_id=>4143310, :@resource_type=>"droplet", :@region=>"nyc3"}>
irb(main):> client.droplet_actions.snapshot(droplet_id: 4143310, name: 'Snapshot Name')
=> "{\"id\":\"unprocessable_entity\",\"message\":\"Droplet is currently on. Please power it off to run this event.\"}"

Like the curl examples above, droplet_kit also returns the Action object in response to a successfully initiated event. It can be accessed as a normal Ruby object. Saving the response into a variable will allow you to access its attributes directly:

irb(main):> snapshot = client.droplet_actions.snapshot(droplet_id: 4143310, name: 'Snapshot Name')
=> "{\"id\":\"unprocessable_entity\",\"message\":\"Droplet is currently on. Please power it off to run this event.\"}"
irb(main):> shutdown = client.droplet_actions.shutdown(droplet_id: 4143310)
=> <DropletKit::Action {:@id=>43919195, :@status=>"in-progress", :@type=>"shutdown", :@started_at=>"2015-02-16T21:32:03Z", :@completed_at=>nil, :@resource_id=>4143310, :@resource_type=>"droplet", :@region=>"nyc3"}>
irb(main):> shutdown.status
=> "in-progress"
irb(main):> shutdown.id
=> 43919195

You can then check the status of the actions:

irb(main):> action = client.actions.find(id: shutdown.id)
=> <DropletKit::Action {:@id=>43919195, :@status=>"completed", :@type=>"shutdown", :@started_at=>"2015-02-16T21:32:03Z", :@completed_at=>"2015-02-16T21:32:07Z", :@resource_id=>4143310, :@resource_type=>"droplet", :@region=>"nyc3"}>
irb(main):> action.status
=> "completed"

We can use an until loop in Ruby to check on the progress of an Action until it has completed:

res = client.droplet_actions.shutdown(droplet_id: id)
until res.status == "completed"
    res = client.actions.find(id: res.id)
    sleep(2)
end

Putting It All Together

This Ruby script bellow is an example of how to check on the status of an action in practice. It powers a droplet off and uses the while loop from above to make sure that the action has completed before moving on. Once the shutdown action has completed, it will then take a snapshot of the droplet.

#!/usr/bin/env ruby

require 'droplet_kit'
require 'json'

token = ENV['DO_TOKEN']
client = DropletKit::Client.new(access_token: token)

droplet_id = ARGV[0]
snapshot_name = ARGV[1] || Time.now.strftime("%b. %d, %Y - %H:%M:%S %Z")

def power_off(client, id)
    res = client.droplet_actions.shutdown(droplet_id: id)
    until res.status == "completed"
        res = client.actions.find(id: res.id)
        sleep(2)
    end
    puts " *   Action status: #{res.status}"
rescue NoMethodError
    puts JSON.parse(res)['message']
end

def take_snapshot(client, id, name)
    res = client.droplet_actions.snapshot(droplet_id: id, name: name)
    puts " *   Action status: #{res.status}"
rescue NameError
    puts JSON.parse(res)['message']
end

unless droplet_id.nil?
    puts "Powering off droplet..."
    power_off(client, droplet_id)
    sleep(2)
    puts "Taking snapshot..."
    take_snapshot(client, droplet_id, snapshot_name)
else
    puts "Power off and snapshot a droplet. Requires a droplet ID and optionally a snapshot name."
    puts "Usage: #{$0} droplet_id ['snapshot name']"
end

If you save this script as a file named snapshot.rb (or download it from this GitHub Gist), you can run it from the command line like so:

DO_TOKEN=YOUR_DO_API_TOKEN ruby snapshot.rb 12345 "My Snapshot"

Note that in order to use the script, you must export your API token as a environmental variable with the name DO_TOKEN. The script takes two arguments, the ID of the droplet and optionally a name of the snapshot. If you do not provide a name, it will is the date and time.

Conclusion

Action items are an important part of the DigtialOcean API. Using them to check the status of actions is an important best practice to implement when using the API. Now that you understand how to use them, you are ready to move on to more complex use-cases of the API like:

Check out the DigitalOcean developer’s portal for more topics.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Andrew SB

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel