{"id":17680002,"url":"https://github.com/costajob/akamai_ccu","last_synced_at":"2025-10-03T15:45:22.777Z","repository":{"id":56842334,"uuid":"94214375","full_name":"costajob/akamai_ccu","owner":"costajob","description":"Minimal wrapper around Akamai CCU V3 APIs.","archived":false,"fork":false,"pushed_at":"2017-08-23T11:30:26.000Z","size":87,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-30T13:15:27.865Z","etag":null,"topics":["akamai","akamai-ccu","akamai-edgerid","cache-control","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/costajob.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-06-13T13:10:51.000Z","updated_at":"2019-04-03T16:19:16.000Z","dependencies_parsed_at":"2022-09-01T06:31:49.113Z","dependency_job_id":null,"html_url":"https://github.com/costajob/akamai_ccu","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/costajob/akamai_ccu","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/costajob%2Fakamai_ccu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/costajob%2Fakamai_ccu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/costajob%2Fakamai_ccu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/costajob%2Fakamai_ccu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/costajob","download_url":"https://codeload.github.com/costajob/akamai_ccu/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/costajob%2Fakamai_ccu/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278187539,"owners_count":25944820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-03T02:00:06.070Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["akamai","akamai-ccu","akamai-edgerid","cache-control","ruby"],"created_at":"2024-10-24T09:05:05.764Z","updated_at":"2025-10-03T15:45:22.736Z","avatar_url":"https://github.com/costajob.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Table of Contents\n\n* [Scope](#scope)\n* [Motivation](#motivation)\n  * [akamai-edgerid](#akamai-edgerid)\n* [Installation](#installation)\n  * [Configuration](#configuration)\n    * [edgerc](#edgerc)\n    * [txt](#txt)\n* [Usage](#usage)\n  * [Library](#library)\n    * [Secret](#secret)\n    * [Edge network](#edge-network)\n    * [Invalidating](#invalidating)\n    * [Deleting](#deleting)\n  * [CLI](#cli)\n    * [Help](#help)\n    * [ccu_invalidate](#ccu_invalidate)\n    * [ccu_delete](#ccu_delete)\n    * [Bulk operation](#bulk-operation)\n    * [Overwriting options](#overwriting-options)\n  * [Logging](#logging)\n    * [Library logger](#library-logger)\n    * [CLI logger](#cli-logger)\n  * [Possible issues](#possible-issues)\n    * [Invalid timestamp](#invalid-timestamp)\n    * [No wildcard](#no-wildcard)\n    * [Mixed bulk](#mixed-bulk)\n\n## Scope\nThis gem is a minimal wrapper of the [Akamai Content Control Utility](https://developer.akamai.com/api/purge/ccu/overview.html) APIs used to purge Edge content by request.  \nThe library is compliant with [CCU API V3](https://developer.akamai.com/api/purge/ccu/resources.html), based on the *Fast Purge Utility*.\n\n## Motivation\nThe gem has two main responsibilities:\n1. sign the request with proper Authorization headers\n2. provide a wrapper around the CCU V3 APIs\n\n### akamai-edgerid\nThere's an official gem by Akamai to sign HTTP headers called [akamai-edgegrid](https://github.com/akamai/AkamaiOPEN-edgegrid-ruby).  \nI've opted to go with my own implementation for the following reasons:\n* the official gem is not written in idiomatic ruby\n* Net::HTTP core class is extended, ignoring composition/decoration\n* single responsibility principle is broken\n* i prefer not relying on external dependencies when possible\n\n## Installation\nAdd this line to your application's Gemfile:\n```ruby\ngem \"akamai_ccu\"\n```\n\nAnd then execute:\n```shell\nbundle\n```\n\nOr install it yourself as:\n```shell\ngem install akamai_ccu\n```\n\n### Configuration\nThis gem requires you have a valid Akamai Luna Control Center account, enabled to add APIs clients.  \nUpon 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.  \nCheck Akamai's [official documentation](https://developer.akamai.com/introduction/Conf_Client.html) for more details.  \nYou have two main options to import credentials data:\n\n#### edgerc\nYou can generate (using a script or by hand) an INI file named `.edgerc`:\n```\n[default]\nclient_secret = xxx=\nhost = akaa-baseurl-xxx-xxx.luna.akamaiapis.net/\naccess_token = akab-access-token-xxx-xxx\nclient_token = akab-client-token-xxx-xxx\nmax-body = 131072\n```\n\n#### txt\nYou can download a plain text file upon credentials data creation:\n```\nclient_secret = xxx=\n\nhost = akaa-baseurl-xxx-xxx.luna.akamaiapis.net/\n\naccess_token = akab-access-token-xxx-xxx\n\nclient_token = akab-client-token-xxx-xxx\n```\n\n## Usage\n\n### Library\nYou can require the gem to use it as a library inside your scripts:\n\n#### Secret\nOnce you've got APIs credentials, you can instantiate the secret object aimed to generate the authorization header:\n```ruby\nrequire \"akamai_ccu\"\n\n# by file, both .edgerc or .txt one\nsecret = AkamaiCCU::Secret.by_file(\"~/tokens.txt\") # default to ~/.edgerc\n\n# by using initializer\nsecret = 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)\n```\n\nThe next step is setting the `Wrapper` class with the secret object, the secret and Net::HTTP client instances are shared between calls:\n```ruby\nAkamaiCCU::Wrapper.setup(secret)\n```\n\n#### Edge network\nPurging actions runs on the `staging` network by default.  \nSwitch to `production` network by just appending a shebang `!` on the method name.\n\n#### Invalidating\nThe CCU V3 APIs allow for invalidating contents by URLs or content provider (CP) codes: currently only the former relies on the Fast Purge Utility.  \n```ruby\n# invalidating resources on staging by URLs\nAkamaiCCU::Wrapper.invalidate_by_url(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/index.html])\n\n# invalidating resources on production (mind the \"!\") by CP code\nAkamaiCCU::Wrapper.invalidate_by_cpcode!([12345,98765])\n```\n\n#### Deleting\nYou can delete contents by URLs or CP codes as well, just be aware of what you're doing:\n```ruby\n# deleting resources on staging by CP codes\nAkamaiCCU::Wrapper.delete_by_cpcode([12345,98765])\n\n# deleting resources on production (mind the \"!\") by URLs\nAkamaiCCU::Wrapper.delete_by_url!(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js])\n```\n\n#### Response\nThe Net::HTTP response is wrapped by an utility struct:\n```ruby\nres = AkamaiCCU::Wrapper.invalidate_by_cpcode([12345,98765])\nputs res \n# 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\n```\n\n### CLI\nYou can use the CLI by:\n\n#### Help\nCalling the help for the specific action:\n```shell\nccu_invalidate -h\nUsage: ccu_invalidate --secret=~/tokens.txt --production --cp=12345,98765\n    -s, --secret=SECRET              Load secret by file (default to ~/.edgerc)\n    -c, --cp=CP                      Specify contents by provider (CP) codes\n    -u, --url=URL                    Specify contents by URLs\n    -b, --bulk=BULK                  Specify bulk contents in a file\n        --headers=HEADERS            Specify any HTTP headers to sign\n    -p, --production                 Purge on production network\n    -h, --help                       Prints this help\n```\n\n#### ccu_invalidate\nDo request for contents invalidation by:\n```shell\nccu_invalidate --secret=~/tokens.txt \\ \n               --url=https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css,https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js \\\n               --production\n```\n\n#### ccu_delete\nDo request for contents deletion by (load secret from ~/.edgerc implicitly):\n```shell\nccu_delete --cp=12345,98765 \\\n           --headers=Accept,Content-Length\n```\n\n#### Bulk operation\nIn case you have multiple contents to work with, it could be impractical to write several entries on the CLI.  \nJust specify them on a separate file and use the bulk option:\n\n`urls.txt` file with URL entries specified on a new line:\n```txt\nhttps://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css\nhttps://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js\nhttps://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/static/index.html\n```\n\nSpecify the bulk option by using the file path:\n```shell\nccu_invalidate --bulk=urls.txt\n```\n\n#### Overwriting options\nThe CLI allows different options to specify the contents to be purged.  \nIf multiple options for contents are provided, the program runs by specific precedence rules:\n\n##### Options precedence\nThe `bulk` option has always precedence over the `cp` one, that has precedence over  `url`:\n\nThis command will invalidate by URLs:\n```shell\nccu_invalidate --secret=~/tokens.txt \\\n               --cp=12345,98765\n               --bulk=urls.txt\n```\n\nThis command will delete by CP codes:\n```shell\nccu_delete --secret=~/tokens.txt \\\n           --cp=12345,98765\n           --url=https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css,https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js\n```\n\n### Logging\n\n#### Library logger\nBy default the `Wrapper` class accepts a logger pointing to `dev/null`. \nIn case you want to replace it with yours, just use the class attribute writer:\n```ruby\nAkamaiCCU::Wrapper.logger = Logger.new(STDOUT)\n```\n\n#### CLI logger\nCLI uses a logger writing to `STDOUT` by default with an `INFO` level.  \nIn case you want to control the log level, just pass an environment variable to the script:\n```shell\nLOG_LEVEL=DEBUG ccu_invalidate --cp=12345,98765\n```\n\n##### Redirecting output\nIn case you're calling the CLI from another program (like your Jenkins script), just redirect the output to your log file:\n```shell\nccu_invalidate --cp=12345,98765 \u003e\u003e mylog.log\n```\n\n### Possible Issues\n\n#### Invalid timestamp\nYou could get a `bad request` response like this:\n```shell\nstatus=400; title=Bad request; detail=Invalid timestamp; support_id=5079982a; described_by=https://problems.purge.akamaiapis.net/-/pep-authn/request-error\n```\n\nThis happens since Akamai APIs only tolerate a clock skew of at most 30 seconds to defend against certain network attacks (described [here](https://community.akamai.com/docs/DOC-1336)).  \nIn order to fix this annoying issue please do synchronize you server clock by:\n* `NTP` versus a stratum 2 server, if you are running on UX OS\n* `manually` versus an [atomic clock site](https://watches.uhrzeit.org/atomic-clock.php) by using your workstation GUI\n\n#### No wildcard\nDo keep in mind CCU V3 APIs doesn't support contents specification by wildcard.\n\n#### Mixed bulk\nWhen specifying contents by bulk on the CLI, you cannot include both CP codes and URLs resources on the same file. \nThe library tries to detect which mode to use basing on entries kind: mixing them will generate unexpected behaviour.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcostajob%2Fakamai_ccu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcostajob%2Fakamai_ccu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcostajob%2Fakamai_ccu/lists"}