Table of Contents

Scope

This gem is a minimal wrapper of the Akamai Content Control Utility APIs used to purge Edge content by request.
The library is compliant with CCU API V3, based on the Fast Purge Utility.

Motivation

The gem has two main responsibilities:

  1. sign the request with proper Authorization headers
  2. provide a wrapper around the CCU V3 APIs

akamai-edgerid

There's an official gem by Akamai to sign HTTP headers called akamai-edgegrid.
I've opted to go with my own implementation for the following reasons:

  • the official gem is not written in idiomatic ruby
  • Net::HTTP core class is extended, ignoring composition/decoration
  • single responsibility principle is broken
  • i prefer not relying on external dependencies when possible

Installation

Add this line to your application's Gemfile:

gem "akamai_ccu"

And then execute:

bundle

Or install it yourself as:

gem install akamai_ccu

Configuration

This gem requires you have a valid Akamai Luna Control Center account, enabled to add APIs clients.
Upon APIs client creation, you'll get the client token to be used to generate new APIs credentials data: these consist of a secret key, two token (client and access) and a dedicated host for API authorization.
Check Akamai's official documentation for more details.
You have two main options to import credentials data:

edgerc

You can generate (using a script or by hand) an INI file named .edgerc:

[default]
client_secret = xxx=
host = akaa-baseurl-xxx-xxx.luna.akamaiapis.net/
access_token = akab-access-token-xxx-xxx
client_token = akab-client-token-xxx-xxx
max-body = 131072

txt

You can download a plain text file upon credentials data creation:

client_secret = xxx=

host = akaa-baseurl-xxx-xxx.luna.akamaiapis.net/

access_token = akab-access-token-xxx-xxx

client_token = akab-client-token-xxx-xxx

Usage

Library

You can require the gem to use it as a library inside your scripts:

Secret

Once you've got APIs credentials, you can instantiate the secret object aimed to generate the authorization header:

require "akamai_ccu"

# by file, both .edgerc or .txt one
secret = AkamaiCCU::Secret.by_file("~/tokens.txt") # default to ~/.edgerc

# by using initializer
secret = AkamaiCCU::Secret.new(client_secret: "xxx=", host: "akaa-baseurl-xxx-xxx.luna.akamaiapis.net/", access_token: "akab-access-token-xxx-xxx", client_token: "akab-client-token-xxx-xxx", max_body: 131072)

The next step is setting the Wrapper class with the secret object, the secret and Net::HTTP client instances are shared between calls:

AkamaiCCU::Wrapper.setup(secret)

Edge network

Purging actions runs on the staging network by default.
Switch to production network by just appending a shebang ! on the method name.

Invalidating

The CCU V3 APIs allow for invalidating contents by URLs or content provider (CP) codes: currently only the former relies on the Fast Purge Utility.

# invalidating resources on staging by URLs
AkamaiCCU::Wrapper.invalidate_by_url(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/index.html])

# invalidating resources on production (mind the "!") by CP code
AkamaiCCU::Wrapper.invalidate_by_cpcode!([12345,98765])

Deleting

You can delete contents by URLs or CP codes as well, just be aware of what you're doing:

# deleting resources on staging by CP codes
AkamaiCCU::Wrapper.delete_by_cpcode([12345,98765])

# deleting resources on production (mind the "!") by URLs
AkamaiCCU::Wrapper.delete_by_url!(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js])

Response

The Net::HTTP response is wrapped by an utility struct:

res = AkamaiCCU::Wrapper.invalidate_by_cpcode([12345,98765])
puts res 
# status=201; detail=Request accepted; support_id=17PY1498402073417329-261436608; purge_id=44ac266e-59b5-11e7-84ca-75d9dd540c3b; copletion_at=2017-06-20 12:19:16 +0100

CLI

You can use the CLI by:

Help

Calling the help for the specific action:

ccu_invalidate -h
Usage: ccu_invalidate --secret=~/tokens.txt --production --cp=12345,98765
    -s, --secret=SECRET              Load secret by file (default to ~/.edgerc)
    -c, --cp=CP                      Specify contents by provider (CP) codes
    -u, --url=URL                    Specify contents by URLs
    -b, --bulk=BULK                  Specify bulk contents in a file
        --headers=HEADERS            Specify any HTTP headers to sign
    -p, --production                 Purge on production network
    -h, --help                       Prints this help

ccu_invalidate

Do request for contents invalidation by:

ccu_invalidate --secret=~/tokens.txt \ 
               --url=https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css,https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js \
               --production

ccu_delete

Do request for contents deletion by (load secret from ~/.edgerc implicitly):

ccu_delete --cp=12345,98765 \
           --headers=Accept,Content-Length

Bulk operation

In case you have multiple contents to work with, it could be impractical to write several entries on the CLI.
Just specify them on a separate file and use the bulk option:

urls.txt file with URL entries specified on a new line:

https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css
https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js
https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/static/index.html

Specify the bulk option by using the file path:

ccu_invalidate --bulk=urls.txt

Overwriting options

The CLI allows different options to specify the contents to be purged.
If multiple options for contents are provided, the program runs by specific precedence rules:

Options precedence

The bulk option has always precedence over the cp one, that has precedence over url:

This command will invalidate by URLs:

ccu_invalidate --secret=~/tokens.txt \
               --cp=12345,98765
               --bulk=urls.txt

This command will delete by CP codes:

ccu_delete --secret=~/tokens.txt \
           --cp=12345,98765
           --url=https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css,https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js

Logging

Library logger

By default the Wrapper class accepts a logger pointing to dev/null. In case you want to replace it with yours, just use the class attribute writer:

AkamaiCCU::Wrapper.logger = Logger.new(STDOUT)

CLI logger

CLI uses a logger writing to STDOUT by default with an INFO level.
In case you want to control the log level, just pass an environment variable to the script:

LOG_LEVEL=DEBUG ccu_invalidate --cp=12345,98765
Redirecting output

In case you're calling the CLI from another program (like your Jenkins script), just redirect the output to your log file:

ccu_invalidate --cp=12345,98765 >> mylog.log

Possible Issues

Invalid timestamp

You could get a bad request response like this:

status=400; title=Bad request; detail=Invalid timestamp; support_id=5079982a; described_by=https://problems.purge.akamaiapis.net/-/pep-authn/request-error

This happens since Akamai APIs only tolerate a clock skew of at most 30 seconds to defend against certain network attacks (described here).
In order to fix this annoying issue please do synchronize you server clock by:

  • NTP versus a stratum 2 server, if you are running on UX OS
  • manually versus an atomic clock site by using your workstation GUI

No wildcard

Do keep in mind CCU V3 APIs doesn't support contents specification by wildcard.

Mixed bulk

When specifying contents by bulk on the CLI, you cannot include both CP codes and URLs resources on the same file. The library tries to detect which mode to use basing on entries kind: mixing them will generate unexpected behaviour.