akamai_ccu 1.5.4 → 1.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -21
- data/lib/akamai_ccu.rb +0 -26
- data/lib/akamai_ccu/cli.rb +1 -1
- data/lib/akamai_ccu/client.rb +5 -0
- data/lib/akamai_ccu/response.rb +15 -60
- data/lib/akamai_ccu/secret.rb +20 -4
- data/lib/akamai_ccu/signer.rb +2 -3
- data/lib/akamai_ccu/version.rb +1 -1
- data/lib/akamai_ccu/wrapper.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21b678bb15fc4484478ffd8eafd454a73d64ebf2
|
4
|
+
data.tar.gz: aea48ed0449fa22043a6c76dc0d04f8a4a26bde8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e84ce88cb3bff7b228c5989e9addf4696415dd3c14909203c6a2393a962c2530754905b4f80c8774fecb52dc21b93bf4c6b509f0e0898ec448d094790f2f89c
|
7
|
+
data.tar.gz: 44aa80033178853ddbf3234a9e2d6d26581637a18e85449736a0d0ad35108a067559141c1ec7bc2851ea455af4e9477bcf837043d149373e65e8ebeb9c5abfe5
|
data/README.md
CHANGED
@@ -18,7 +18,6 @@
|
|
18
18
|
* [ccu_invalidate](#ccu_invalidate)
|
19
19
|
* [ccu_delete](#ccu_delete)
|
20
20
|
* [Bulk operation](#bulk-operation)
|
21
|
-
* [Redirecting output](#redirecting-output)
|
22
21
|
* [Overwriting options](#overwriting-options)
|
23
22
|
* [Logging](#logging)
|
24
23
|
* [Library logger](#library-logger)
|
@@ -123,14 +122,14 @@ The CCU V3 APIs allow for invalidating contents by URLs or content provider (CP)
|
|
123
122
|
AkamaiCCU::Wrapper.invalidate_by_url(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/index.html])
|
124
123
|
|
125
124
|
# invalidating resources on production (mind the "!") by CP code
|
126
|
-
AkamaiCCU::Wrapper.invalidate_by_cpcode!([12345,
|
125
|
+
AkamaiCCU::Wrapper.invalidate_by_cpcode!([12345,98765])
|
127
126
|
```
|
128
127
|
|
129
128
|
#### Deleting
|
130
129
|
You can delete contents by URLs or CP codes as well, just be aware of what you're doing:
|
131
130
|
```ruby
|
132
131
|
# deleting resources on staging by CP codes
|
133
|
-
AkamaiCCU::Wrapper.delete_by_cpcode([12345,
|
132
|
+
AkamaiCCU::Wrapper.delete_by_cpcode([12345,98765])
|
134
133
|
|
135
134
|
# deleting resources on production (mind the "!") by URLs
|
136
135
|
AkamaiCCU::Wrapper.delete_by_url!(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js])
|
@@ -139,9 +138,9 @@ AkamaiCCU::Wrapper.delete_by_url!(%w[https://akaa-baseurl-xxx-xxx.luna.akamaiapi
|
|
139
138
|
#### Response
|
140
139
|
The Net::HTTP response is wrapped by an utility struct:
|
141
140
|
```ruby
|
142
|
-
res = AkamaiCCU::Wrapper.invalidate_by_cpcode([12345,
|
141
|
+
res = AkamaiCCU::Wrapper.invalidate_by_cpcode([12345,98765])
|
143
142
|
puts res
|
144
|
-
# status=201; detail=Request accepted; purge_id=
|
143
|
+
# 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
|
145
144
|
```
|
146
145
|
|
147
146
|
### CLI
|
@@ -151,12 +150,12 @@ You can use the CLI by:
|
|
151
150
|
Calling the help for the specific action:
|
152
151
|
```shell
|
153
152
|
ccu_invalidate -h
|
154
|
-
Usage: ccu_invalidate --secret
|
155
|
-
-s, --secret=SECRET Load secret
|
153
|
+
Usage: ccu_invalidate --secret=~/tokens.txt --production --cp=12345,98765
|
154
|
+
-s, --secret=SECRET Load secret by file (default to ~/.edgerc)
|
156
155
|
-c, --cp=CP Specify contents by provider (CP) codes
|
157
156
|
-u, --url=URL Specify contents by URLs
|
158
157
|
-b, --bulk=BULK Specify bulk contents in a file
|
159
|
-
--headers=HEADERS Specify HTTP headers to sign
|
158
|
+
--headers=HEADERS Specify any HTTP headers to sign
|
160
159
|
-p, --production Purge on production network
|
161
160
|
-h, --help Prints this help
|
162
161
|
```
|
@@ -164,16 +163,15 @@ Usage: ccu_invalidate --secret=~/.edgerc --production --cp=12345,98765
|
|
164
163
|
#### ccu_invalidate
|
165
164
|
Do request for contents invalidation by:
|
166
165
|
```shell
|
167
|
-
ccu_invalidate --secret
|
166
|
+
ccu_invalidate --secret=~/tokens.txt \
|
168
167
|
--url=https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.css,https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/main.js \
|
169
168
|
--production
|
170
169
|
```
|
171
170
|
|
172
171
|
#### ccu_delete
|
173
|
-
Do request for contents deletion by:
|
172
|
+
Do request for contents deletion by (load secret from ~/.edgerc implicitly):
|
174
173
|
```shell
|
175
|
-
ccu_delete --
|
176
|
-
--cp=12345,98765 \
|
174
|
+
ccu_delete --cp=12345,98765 \
|
177
175
|
--headers=Accept,Content-Length
|
178
176
|
```
|
179
177
|
|
@@ -190,13 +188,7 @@ https://akaa-baseurl-xxx-xxx.luna.akamaiapis.net/static/index.html
|
|
190
188
|
|
191
189
|
Specify the bulk option by using the file path:
|
192
190
|
```shell
|
193
|
-
ccu_invalidate --
|
194
|
-
```
|
195
|
-
|
196
|
-
#### Redirecting output
|
197
|
-
In case you're calling the CLI from another program (like your Jenkins script), just redirect the output to your log file:
|
198
|
-
```shell
|
199
|
-
ccu_invalidate --secret=~/.edgerc --cp=12345,98765 >> mylog.log
|
191
|
+
ccu_invalidate --bulk=urls.txt
|
200
192
|
```
|
201
193
|
|
202
194
|
#### Overwriting options
|
@@ -233,7 +225,13 @@ AkamaiCCU::Wrapper.logger = Logger.new(STDOUT)
|
|
233
225
|
CLI uses a logger writing to `STDOUT` by default with an `INFO` level.
|
234
226
|
In case you want to control the log level, just pass an environment variable to the script:
|
235
227
|
```shell
|
236
|
-
LOG_LEVEL=DEBUG ccu_invalidate --
|
228
|
+
LOG_LEVEL=DEBUG ccu_invalidate --cp=12345,98765
|
229
|
+
```
|
230
|
+
|
231
|
+
##### Redirecting output
|
232
|
+
In case you're calling the CLI from another program (like your Jenkins script), just redirect the output to your log file:
|
233
|
+
```shell
|
234
|
+
ccu_invalidate --cp=12345,98765 >> mylog.log
|
237
235
|
```
|
238
236
|
|
239
237
|
### Possible Issues
|
@@ -241,7 +239,7 @@ LOG_LEVEL=DEBUG ccu_invalidate --secret=~/.edgerc --cp=12345,98765
|
|
241
239
|
#### Invalid timestamp
|
242
240
|
You could get a `bad request` response like this:
|
243
241
|
```shell
|
244
|
-
status=400; title=Bad request; detail=Invalid timestamp;
|
242
|
+
status=400; title=Bad request; detail=Invalid timestamp; support_id=5079982a; described_by=https://problems.purge.akamaiapis.net/-/pep-authn/request-error
|
245
243
|
```
|
246
244
|
|
247
245
|
This 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)).
|
data/lib/akamai_ccu.rb
CHANGED
@@ -1,31 +1,5 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
2
|
|
3
|
-
require "base64"
|
4
|
-
require "json"
|
5
|
-
require "openssl"
|
6
3
|
require "akamai_ccu/version"
|
7
4
|
require "akamai_ccu/wrapper"
|
8
5
|
require "akamai_ccu/cli"
|
9
|
-
|
10
|
-
module AkamaiCCU
|
11
|
-
extend self
|
12
|
-
|
13
|
-
GET = :Get
|
14
|
-
POST = :Post
|
15
|
-
SSL = "https"
|
16
|
-
JSON_HEADER = { "Content-Type" => "application/json" }
|
17
|
-
|
18
|
-
def format_utc(time)
|
19
|
-
time.utc.strftime("%Y%m%dT%H:%M:%S+0000")
|
20
|
-
end
|
21
|
-
|
22
|
-
def sign(data)
|
23
|
-
digest = OpenSSL::Digest::SHA256.new.digest(data)
|
24
|
-
Base64.encode64(digest).strip
|
25
|
-
end
|
26
|
-
|
27
|
-
def sign_HMAC(key:, data:)
|
28
|
-
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data)
|
29
|
-
Base64.encode64(digest).strip
|
30
|
-
end
|
31
|
-
end
|
data/lib/akamai_ccu/cli.rb
CHANGED
@@ -49,7 +49,7 @@ module AkamaiCCU
|
|
49
49
|
|
50
50
|
private def parser
|
51
51
|
OptionParser.new do |opts|
|
52
|
-
opts.banner = "Usage: ccu_#{@action} --secret
|
52
|
+
opts.banner = "Usage: ccu_#{@action} --secret=~/tokens.txt --production --cp=12345,98765"
|
53
53
|
|
54
54
|
opts.on("-sSECRET", "--secret=SECRET", "Load secret by file (default to ~/.edgerc)") do |secret|
|
55
55
|
@secret = File.expand_path(secret)
|
data/lib/akamai_ccu/client.rb
CHANGED
data/lib/akamai_ccu/response.rb
CHANGED
@@ -4,83 +4,38 @@ module AkamaiCCU
|
|
4
4
|
class Response
|
5
5
|
BAD_STATUS = 400
|
6
6
|
|
7
|
-
|
8
|
-
response = new(body)
|
9
|
-
case response
|
10
|
-
when ->(res) { res.successful? }
|
11
|
-
Ack.new(body)
|
12
|
-
else
|
13
|
-
Error.new(body)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_reader :body, :status, :detail
|
7
|
+
attr_reader :body, :title, :status, :detail, :support_id, :purge_id, :decribed_by, :completion_at
|
18
8
|
|
19
|
-
def initialize(body = {})
|
9
|
+
def initialize(body = {}, time = Time.now)
|
20
10
|
@body = parse(body)
|
11
|
+
@title = @body["title"]
|
21
12
|
@status = @body.fetch("httpStatus") { @body.fetch("status", BAD_STATUS) }
|
22
13
|
@detail = @body["detail"]
|
14
|
+
@support_id = @body.fetch("supportId") { @body["requestId"] }
|
15
|
+
@purge_id = @body["purgeId"]
|
16
|
+
@described_by = @body.fetch("describedBy") { @body["type"] }
|
17
|
+
@estimated_secs = @body["estimatedSeconds"]
|
18
|
+
@completion_at = time + @estimated_secs.to_i if @estimated_secs
|
23
19
|
end
|
24
20
|
|
25
21
|
def successful?
|
26
22
|
(@status.to_i / 100) == 2
|
27
23
|
end
|
28
24
|
|
29
|
-
private def parse(body)
|
30
|
-
return body if body.is_a? Hash
|
31
|
-
JSON.parse(body)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class Error < Response
|
36
|
-
attr_reader :type, :title, :request_id, :instance, :method, :server_ip, :client_ip
|
37
|
-
|
38
|
-
def initialize(body)
|
39
|
-
super(body)
|
40
|
-
@type = @body["type"]
|
41
|
-
@title = @body["title"]
|
42
|
-
@request_id = @body["requestId"]
|
43
|
-
@instance = @body["instance"]
|
44
|
-
@method = @body["method"]
|
45
|
-
@serverIp = @body["serverIp"]
|
46
|
-
@clientIp = @body["clientIp"]
|
47
|
-
@requested_at = @body["requestTime"]
|
48
|
-
end
|
49
|
-
|
50
|
-
def requested_at
|
51
|
-
return nil unless @requested_at
|
52
|
-
Time.parse(@requested_at)
|
53
|
-
end
|
54
|
-
|
55
25
|
def to_s
|
56
26
|
%W[status=#{@status}].tap do |a|
|
57
27
|
a << "title=#{@title}" if @title
|
58
28
|
a << "detail=#{@detail}" if @detail
|
59
|
-
a << "
|
60
|
-
a << "
|
61
|
-
a << "
|
29
|
+
a << "support_id=#{@support_id}" if @support_id
|
30
|
+
a << "purge_id=#{@purge_id}" if @purge_id
|
31
|
+
a << "described_by=#{@described_by}" if @described_by
|
32
|
+
a << "copletion_at=#{@completion_at}" if @completion_at
|
62
33
|
end.join("; ")
|
63
34
|
end
|
64
|
-
end
|
65
35
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def initialize(body, time = Time.now)
|
70
|
-
super(body)
|
71
|
-
@purge_id = @body["purgeId"]
|
72
|
-
@estimated_secs = @body["estimatedSeconds"]
|
73
|
-
@support_id = @body["supportId"]
|
74
|
-
@completion_at = time + @estimated_secs.to_i
|
75
|
-
end
|
76
|
-
|
77
|
-
def to_s
|
78
|
-
%W[status=#{@status}].tap do |a|
|
79
|
-
a << "detail=#{@detail}" if @detail
|
80
|
-
a << "purge_id=#{@purge_id}" if @purge_id
|
81
|
-
a << "support_id=#{@support_id}" if @support_id
|
82
|
-
a << "copletion_at=#{AkamaiCCU.format_utc(@completion_at)}" if @completion_at
|
83
|
-
end.join("; ")
|
36
|
+
private def parse(body)
|
37
|
+
return body if body.is_a? Hash
|
38
|
+
JSON.parse(body)
|
84
39
|
end
|
85
40
|
end
|
86
41
|
end
|
data/lib/akamai_ccu/secret.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
require "
|
1
|
+
require "base64"
|
2
|
+
require "openssl"
|
2
3
|
require "securerandom"
|
4
|
+
require "uri"
|
3
5
|
|
4
6
|
module AkamaiCCU
|
5
7
|
class Secret
|
@@ -9,6 +11,20 @@ module AkamaiCCU
|
|
9
11
|
|
10
12
|
class FileContentError < ArgumentError; end
|
11
13
|
|
14
|
+
def self.format_utc(time)
|
15
|
+
time.utc.strftime("%Y%m%dT%H:%M:%S+0000")
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.sign(data)
|
19
|
+
digest = OpenSSL::Digest::SHA256.new.digest(data)
|
20
|
+
Base64.encode64(digest).strip
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.sign_HMAC(key, data)
|
24
|
+
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, key, data)
|
25
|
+
Base64.encode64(digest).strip
|
26
|
+
end
|
27
|
+
|
12
28
|
def self.by_file(name = "~/.edgerc", time = Time.now)
|
13
29
|
path = File.expand_path(name)
|
14
30
|
return unless File.exist?(path)
|
@@ -32,17 +48,17 @@ module AkamaiCCU
|
|
32
48
|
@client_token = client_token
|
33
49
|
@max_body = max_body.to_i
|
34
50
|
@nonce = nonce
|
35
|
-
@timestamp =
|
51
|
+
@timestamp = self.class.format_utc(time)
|
36
52
|
end
|
37
53
|
|
38
54
|
def touch
|
39
55
|
@nonce = SecureRandom.uuid
|
40
|
-
@timestamp =
|
56
|
+
@timestamp = self.class.format_utc(Time.now)
|
41
57
|
self
|
42
58
|
end
|
43
59
|
|
44
60
|
def signed_key
|
45
|
-
|
61
|
+
self.class.sign_HMAC(@client_secret, @timestamp)
|
46
62
|
end
|
47
63
|
|
48
64
|
def auth_header
|
data/lib/akamai_ccu/signer.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "forwardable"
|
2
|
-
require "uri"
|
3
2
|
require "akamai_ccu/secret"
|
4
3
|
|
5
4
|
module AkamaiCCU
|
@@ -42,7 +41,7 @@ module AkamaiCCU
|
|
42
41
|
private def signed_body
|
43
42
|
return "" unless body?
|
44
43
|
truncated = body[0...max_body]
|
45
|
-
|
44
|
+
@secret.class.sign(truncated)
|
46
45
|
end
|
47
46
|
|
48
47
|
private def signature_data
|
@@ -58,7 +57,7 @@ module AkamaiCCU
|
|
58
57
|
end
|
59
58
|
|
60
59
|
private def signature
|
61
|
-
|
60
|
+
@secret.class.sign_HMAC(signed_key, signature_data.join(TAB))
|
62
61
|
end
|
63
62
|
|
64
63
|
def signed_header
|
data/lib/akamai_ccu/version.rb
CHANGED
data/lib/akamai_ccu/wrapper.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "json"
|
1
2
|
require "akamai_ccu/client"
|
2
3
|
require "akamai_ccu/endpoint"
|
3
4
|
require "akamai_ccu/signer"
|
@@ -45,7 +46,7 @@ module AkamaiCCU
|
|
45
46
|
self.class.logger.debug { "request: uri=#{request.path}; body=#{request.body}; authorization=#{request["Authorization"]}" }
|
46
47
|
end
|
47
48
|
self.class.logger.debug { "response: inspect=#{response.inspect}; body=#{response.body}" }
|
48
|
-
response_klass.
|
49
|
+
response_klass.new(response.body)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: akamai_ccu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- costajob
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-06-
|
11
|
+
date: 2017-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|