fog-akamai 0.1.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/Gemfile +1 -1
- data/README.md +1 -3
- data/Rakefile +1 -1
- data/bin/console +3 -3
- data/fog-akamai.gemspec +5 -3
- data/lib/fog/akamai/models/storage/directories.rb +21 -3
- data/lib/fog/akamai/models/storage/directory.rb +25 -3
- data/lib/fog/akamai/models/storage/file.rb +45 -33
- data/lib/fog/akamai/models/storage/files.rb +3 -2
- data/lib/fog/akamai/parsers/storage/dir.rb +13 -13
- data/lib/fog/akamai/parsers/storage/du.rb +29 -0
- data/lib/fog/akamai/requests/storage/delete.rb +46 -0
- data/lib/fog/akamai/requests/storage/dir.rb +24 -11
- data/lib/fog/akamai/requests/storage/download.rb +14 -7
- data/lib/fog/akamai/requests/storage/du.rb +42 -0
- data/lib/fog/akamai/requests/storage/mkdir.rb +52 -0
- data/lib/fog/akamai/requests/storage/mtime.rb +45 -0
- data/lib/fog/akamai/requests/storage/rename.rb +54 -0
- data/lib/fog/akamai/requests/storage/rmdir.rb +41 -0
- data/lib/fog/akamai/requests/storage/stat.rb +49 -9
- data/lib/fog/akamai/requests/storage/symlink.rb +25 -0
- data/lib/fog/akamai/requests/storage/upload.rb +35 -11
- data/lib/fog/akamai/storage.rb +75 -30
- data/lib/fog/akamai/version.rb +1 -1
- data/lib/fog/akamai.rb +1 -3
- metadata +40 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6c27c6eba6ca0f38e804c4eb82ebb102367e1eb
|
4
|
+
data.tar.gz: b2f945bf7fdb1be2e4bb987c31fc1af89be4ef8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4f0605c05d091ce683511b8363ed4d07c9a737df8950373b91f8a38ebca998c9b7b741010a2e327579428ce6d44bcaaccb24887381960824380f5b20d77408c
|
7
|
+
data.tar.gz: 163d6e859734fb81e94c6f9897adc7a125c436f1339b38a4b0edc18119adac2ed84cb3e22cc879ef8e8ed78738e29b6104ae990fc66ce396f1fd49a0f2444e97
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/fog/storage`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
4
|
|
5
|
-
|
5
|
+
All the functionality of the net storage api is implemented except the really dangerous bit, quick-delete.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -66,8 +66,6 @@ directory.files.create(directory: directory, body: file_body, key: file_name)
|
|
66
66
|
|
67
67
|
## Roadmap
|
68
68
|
|
69
|
-
- finish implementing the netstorage api
|
70
|
-
- add mock support
|
71
69
|
- implement ccu api
|
72
70
|
|
73
71
|
## Development
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'fog/akamai'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "fog/akamai"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/fog-akamai.gemspec
CHANGED
@@ -10,9 +10,9 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ['calinoiu.alexandru@agilefreaks.com']
|
11
11
|
spec.license = 'MIT'
|
12
12
|
|
13
|
-
spec.summary =
|
14
|
-
spec.description =
|
15
|
-
to use Akamai
|
13
|
+
spec.summary = "Module for 'fog' gem to support Akamai"
|
14
|
+
spec.description = 'This library can be used as a module for `fog` or as standalone provider
|
15
|
+
to use Akamai'
|
16
16
|
spec.homepage = 'https://github.com/alexandru-calinoiu/fog-akamai'
|
17
17
|
|
18
18
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -26,6 +26,8 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
27
27
|
spec.add_development_dependency 'test-unit', '~> 3.1'
|
28
28
|
spec.add_development_dependency 'webmock', '~> 1.22'
|
29
|
+
spec.add_development_dependency 'rubocop', '~> 0.35'
|
30
|
+
spec.add_development_dependency 'timecop', '~> 0.8.0'
|
29
31
|
|
30
32
|
spec.add_dependency 'fog-core', '~> 1.27'
|
31
33
|
spec.add_dependency 'fog-json', '~> 1.0'
|
@@ -4,15 +4,33 @@ module Fog
|
|
4
4
|
class Directories < Fog::Collection
|
5
5
|
model Fog::Storage::Akamai::Directory
|
6
6
|
|
7
|
+
attribute :parent
|
8
|
+
|
9
|
+
def all
|
10
|
+
requires :parent
|
11
|
+
parent.directories
|
12
|
+
end
|
13
|
+
|
7
14
|
def get(key)
|
8
15
|
data = service.dir(key).body
|
9
16
|
|
10
|
-
directory = new(:
|
11
|
-
|
17
|
+
directory = new(key: data[:directory].sub("/#{service.akamai_cp_code}", ''))
|
18
|
+
load_files(directory, data)
|
19
|
+
load_directories(directory, data)
|
12
20
|
|
13
21
|
directory
|
14
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def load_files(directory, data)
|
27
|
+
directory.files.load(data[:files].map { |file| file.merge(directory: directory) })
|
28
|
+
end
|
29
|
+
|
30
|
+
def load_directories(directory, data)
|
31
|
+
directory.directories.load(data[:directories].map { |dir| dir.merge(parent: directory) })
|
32
|
+
end
|
15
33
|
end
|
16
34
|
end
|
17
35
|
end
|
18
|
-
end
|
36
|
+
end
|
@@ -2,16 +2,38 @@ module Fog
|
|
2
2
|
module Storage
|
3
3
|
class Akamai
|
4
4
|
class Directory < Fog::Model
|
5
|
-
|
5
|
+
include Fog::Akamai::Shared
|
6
|
+
|
7
|
+
identity :key, aliases: 'name'
|
6
8
|
|
7
9
|
attribute :parent
|
10
|
+
attribute :name
|
11
|
+
attribute :mtime
|
8
12
|
|
9
13
|
def files
|
10
14
|
@files ||= Fog::Storage::Akamai::Files.new(
|
11
|
-
:
|
12
|
-
:
|
15
|
+
directory: self,
|
16
|
+
service: service
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def directories
|
21
|
+
@directories ||= Fog::Storage::Akamai::Directories.new(
|
22
|
+
parent: self,
|
23
|
+
service: service
|
13
24
|
)
|
14
25
|
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
requires :key
|
29
|
+
path = parent.nil? ? key : full_path(key, parent)
|
30
|
+
service.mkdir(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy
|
34
|
+
requires :key
|
35
|
+
service.rmdir(key)
|
36
|
+
end
|
15
37
|
end
|
16
38
|
end
|
17
39
|
end
|
@@ -1,35 +1,47 @@
|
|
1
1
|
module Fog
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
2
|
+
module Storage
|
3
|
+
class Akamai
|
4
|
+
class File < Fog::Model
|
5
|
+
include Fog::Akamai::Shared
|
6
|
+
|
7
|
+
identity :key, aliases: 'name'
|
8
|
+
|
9
|
+
attribute :directory
|
10
|
+
attribute :name
|
11
|
+
attribute :mtime
|
12
|
+
attribute :md5
|
13
|
+
attribute :size
|
14
|
+
attribute :body
|
15
|
+
|
16
|
+
def get(_key)
|
17
|
+
requires :directory
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
requires :body, :directory, :key
|
22
|
+
service.upload(full_path(key, directory), body)
|
23
|
+
end
|
24
|
+
|
25
|
+
def destroy
|
26
|
+
requires :directory, :key
|
27
|
+
end
|
28
|
+
|
29
|
+
def ready?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def touch(mtime = DateTime.now.to_time.to_i)
|
34
|
+
requires :directory, :key
|
35
|
+
service.mtime(full_path(key, directory), mtime)
|
36
|
+
end
|
37
|
+
|
38
|
+
def rename(new_name)
|
39
|
+
requires :directory, :key
|
40
|
+
service.rename(full_path(key, directory), full_path(new_name, directory))
|
41
|
+
self.key = new_name
|
42
|
+
self.name = new_name
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
35
47
|
end
|
@@ -10,11 +10,12 @@ module Fog
|
|
10
10
|
|
11
11
|
def all
|
12
12
|
requires :directory
|
13
|
+
directory.files
|
13
14
|
end
|
14
15
|
|
15
16
|
def get(path)
|
16
17
|
body = service.download(full_path(path, directory)).data[:body]
|
17
|
-
new(:
|
18
|
+
new(body: body)
|
18
19
|
end
|
19
20
|
|
20
21
|
def stat(path)
|
@@ -25,4 +26,4 @@ module Fog
|
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
28
|
-
end
|
29
|
+
end
|
@@ -4,30 +4,30 @@ module Fog
|
|
4
4
|
module Akamai
|
5
5
|
class Dir < Fog::Parsers::Base
|
6
6
|
def reset
|
7
|
-
@response = { :
|
7
|
+
@response = { directory: '', files: [], directories: [] }
|
8
8
|
end
|
9
9
|
|
10
10
|
def start_element(name, attrs = [])
|
11
11
|
case name
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
when 'stat'
|
13
|
+
@response[:directory] = attrs.first.value
|
14
|
+
when 'file'
|
15
|
+
@response[:files] << attrs_to_hash(attrs) if of_type?(attrs, 'file')
|
16
|
+
@response[:directories] << attrs_to_hash(attrs) if of_type?(attrs, 'dir')
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def attrs_to_hash(attrs)
|
26
|
-
attrs.inject({}) { |result, attr| result.merge(attr.localname => attr.value) }
|
27
|
-
end
|
22
|
+
def of_type?(attrs, type)
|
23
|
+
attrs.any? { |attr| attr.localname == 'type' && attr.value == type }
|
24
|
+
end
|
28
25
|
|
26
|
+
def attrs_to_hash(attrs)
|
27
|
+
attrs.inject({}) { |a, e| a.merge(e.localname => e.value) }
|
28
|
+
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
|
-
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fog
|
2
|
+
module Parsers
|
3
|
+
module Storage
|
4
|
+
module Akamai
|
5
|
+
class Du < Fog::Parsers::Base
|
6
|
+
def reset
|
7
|
+
@response = { directory: '', files: '', bytes: '' }
|
8
|
+
end
|
9
|
+
|
10
|
+
def start_element(name, attrs = [])
|
11
|
+
case name
|
12
|
+
when 'du'
|
13
|
+
@response[:directory] = value_for_attr(attrs, 'directory')
|
14
|
+
when 'du-info'
|
15
|
+
@response[:files] = value_for_attr(attrs, 'files')
|
16
|
+
@response[:bytes] = value_for_attr(attrs, 'bytes')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def value_for_attr(attrs, name)
|
23
|
+
attrs.find { |attr| attr.localname == name }.value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Akamai
|
6
|
+
class Real
|
7
|
+
# Use this action to delete an individual file or symbolic link.
|
8
|
+
# @param path [String] the path for the file that will be downloaded
|
9
|
+
# @return [Excon::Response] response
|
10
|
+
|
11
|
+
def delete(path)
|
12
|
+
path_guard(path)
|
13
|
+
request(:delete,
|
14
|
+
path: format_path(path),
|
15
|
+
method: 'PUT',
|
16
|
+
expects: 200)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Mock
|
21
|
+
def delete(path)
|
22
|
+
path_guard(path)
|
23
|
+
|
24
|
+
if remove_file(Pathname.new(format_path(path)))
|
25
|
+
Excon::Response.new(status: 200)
|
26
|
+
else
|
27
|
+
fail(Excon::Errors::NotFound, '404 Not Found')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def remove_file(path)
|
34
|
+
return false unless data.key?(path.to_s)
|
35
|
+
|
36
|
+
data[path.to_s] = nil
|
37
|
+
remove_file_from_parent_dir(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_file_from_parent_dir(path)
|
41
|
+
data[path.split.first.to_s][:files].reject! { |hash| hash['name'] == path.basename.to_s }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -4,23 +4,36 @@ module Fog
|
|
4
4
|
class Real
|
5
5
|
require 'fog/akamai/parsers/storage/dir'
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# Use this action to return the structure for a selected directory
|
8
|
+
# @return [Excon::Response] response:
|
9
|
+
# * body [Hash]:
|
10
|
+
# * directory [String] - Path to directory
|
11
|
+
# * files [Array]:
|
12
|
+
# * type [String]
|
13
|
+
# * name [String]
|
14
|
+
# * mtime [String]
|
15
|
+
# * size [String]
|
16
|
+
# * md5 [String]
|
17
|
+
# * directories [Array]:
|
18
|
+
# * type [String]
|
19
|
+
# * name [String]
|
20
|
+
# * mtime [String]
|
21
|
+
|
22
|
+
def dir(path = '')
|
9
23
|
request(:dir,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
:parser => Fog::Parsers::Storage::Akamai::Dir.new
|
15
|
-
})
|
24
|
+
path: format_path(path),
|
25
|
+
method: 'GET',
|
26
|
+
expects: 200,
|
27
|
+
parser: Fog::Parsers::Storage::Akamai::Dir.new)
|
16
28
|
end
|
17
29
|
end
|
18
30
|
|
19
31
|
class Mock
|
20
|
-
def dir
|
21
|
-
|
32
|
+
def dir(path = '')
|
33
|
+
key = format_path(path)
|
34
|
+
data.key?(key) ? Excon::Response.new(status: 200, body: data[key]) : fail(Excon::Errors::NotFound, '404 Not Found')
|
22
35
|
end
|
23
36
|
end
|
24
37
|
end
|
25
38
|
end
|
26
|
-
end
|
39
|
+
end
|
@@ -2,21 +2,28 @@ module Fog
|
|
2
2
|
module Storage
|
3
3
|
class Akamai
|
4
4
|
class Real
|
5
|
+
# Use this action to download a file
|
6
|
+
# @param path [String] the path for the file that will be downloaded
|
7
|
+
# @return [Excon::Response] response:
|
8
|
+
# * body [binary]
|
9
|
+
|
5
10
|
def download(path)
|
11
|
+
path_guard(path)
|
6
12
|
request(:download,
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
:expects => 200
|
11
|
-
})
|
13
|
+
path: format_path(path),
|
14
|
+
method: 'GET',
|
15
|
+
expects: 200)
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
19
|
class Mock
|
16
20
|
def download(path)
|
17
|
-
|
21
|
+
path_guard(path)
|
22
|
+
formatted_path = format_path(path)
|
23
|
+
fail(Excon::Errors::NotFound, '404 Not Found') unless data.key?(formatted_path) && data[formatted_path].key?(:body)
|
24
|
+
Excon::Response.new(status: 200, body: data[formatted_path][:body])
|
18
25
|
end
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
22
|
-
end
|
29
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Akamai
|
4
|
+
class Real
|
5
|
+
require 'fog/akamai/parsers/storage/du'
|
6
|
+
|
7
|
+
# Use this action to return disk usage information for the directory specified by the
|
8
|
+
# @path, including all files stored in any sub-directories that may exist.
|
9
|
+
# @param path [String] the path for the file that will be downloaded
|
10
|
+
# @return [Excon::Response] response:
|
11
|
+
# * body [Hash]:
|
12
|
+
# * directory [String] - The path to the directory
|
13
|
+
# * files [String] - The size of the files in bytes
|
14
|
+
# * bytes [String] - The size of the directory in bytes
|
15
|
+
|
16
|
+
def du(path)
|
17
|
+
path_guard(path)
|
18
|
+
request(:du,
|
19
|
+
path: format_path(path),
|
20
|
+
method: 'GET',
|
21
|
+
expects: 200,
|
22
|
+
parser: Fog::Parsers::Storage::Akamai::Du.new)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Mock
|
27
|
+
def du(path)
|
28
|
+
path_guard(path)
|
29
|
+
|
30
|
+
key = format_path(path)
|
31
|
+
directory = data[key]
|
32
|
+
|
33
|
+
if directory
|
34
|
+
Excon::Response.new(status: 200, body: { directory: key, files: directory[:files].count.to_s, bytes: directory[:directories].count.to_s })
|
35
|
+
else
|
36
|
+
fail(Excon::Errors::NotFound, '404 Not Found')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Akamai
|
6
|
+
class Real
|
7
|
+
# Use this action to create a dir
|
8
|
+
# @param path [String] the path to create, it will create directories recursively
|
9
|
+
# @return [Excon::Response] response
|
10
|
+
|
11
|
+
def mkdir(path)
|
12
|
+
path_guard(path)
|
13
|
+
request(:mkdir,
|
14
|
+
path: format_path(path),
|
15
|
+
method: 'PUT',
|
16
|
+
expects: 200
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mock
|
22
|
+
def mkdir(path)
|
23
|
+
path_guard(path)
|
24
|
+
|
25
|
+
path = Pathname.new(format_path(path))
|
26
|
+
last_path_basename = ''
|
27
|
+
|
28
|
+
path.ascend do |parent|
|
29
|
+
break if parent.nil?
|
30
|
+
|
31
|
+
key = parent.to_s
|
32
|
+
update_data(key, last_path_basename)
|
33
|
+
last_path_basename = parent.basename.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
Excon::Response.new(headers: { 'Status' => 200 })
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def update_data(key, last_path_basename)
|
42
|
+
data[key] ||= { directory: key, files: [], directories: [] }
|
43
|
+
data[key][:directories] << build_directory_node(last_path_basename) unless last_path_basename.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_directory_node(last_path_basename)
|
47
|
+
{ 'type' => 'dir', 'name' => last_path_basename, 'mtime' => DateTime.now.to_time.to_i.to_s }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Akamai
|
6
|
+
class Real
|
7
|
+
# Use this action to change a file's modification time ("touch").
|
8
|
+
# @param path [String] the path for he file that will be downloaded
|
9
|
+
# @param mtime [int] the desired modification time for the target content (i.e., in UNIX epoch time).
|
10
|
+
# @return [Excon::Response] response
|
11
|
+
|
12
|
+
def mtime(path, mtime = DateTime.now.to_time.to_i)
|
13
|
+
path_guard(path)
|
14
|
+
request({ action: :mtime, mtime: mtime },
|
15
|
+
path: format_path(path),
|
16
|
+
method: 'POST',
|
17
|
+
expects: 200)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mock
|
22
|
+
def mtime(path, mtime = DateTime.now.to_time.to_i)
|
23
|
+
path_guard(path)
|
24
|
+
|
25
|
+
pathname = Pathname.new(format_path(path))
|
26
|
+
file = get_file(pathname)
|
27
|
+
|
28
|
+
if file
|
29
|
+
file['mtime'] = mtime.to_s
|
30
|
+
Excon::Response.new(status: 200)
|
31
|
+
else
|
32
|
+
fail(Excon::Errors::NotFound, '404 Not Found')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def get_file(pathname)
|
39
|
+
dir = data[pathname.split.first.to_s] || { files: [] }
|
40
|
+
dir[:files].find { |file_hash| file_hash['name'] == pathname.basename.to_s }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Fog
|
4
|
+
module Storage
|
5
|
+
class Akamai
|
6
|
+
class Real
|
7
|
+
# Use this action to rename a file or symbolic link.
|
8
|
+
# @param source [String] the path to check
|
9
|
+
# @param destination [String] the path to check
|
10
|
+
# @return [Excon::Response] response
|
11
|
+
|
12
|
+
def rename(source, destination)
|
13
|
+
path_guard(source)
|
14
|
+
path_guard(destination)
|
15
|
+
|
16
|
+
request({ action: :rename, destination: CGI.escape(format_path(destination)) },
|
17
|
+
path: format_path(source),
|
18
|
+
method: 'POST',
|
19
|
+
expects: 200)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Mock
|
24
|
+
def rename(source, destination)
|
25
|
+
path_guard(source)
|
26
|
+
path_guard(destination)
|
27
|
+
|
28
|
+
source_pathname = Pathname.new(format_path(source))
|
29
|
+
destination_pathname = Pathname.new(format_path(destination))
|
30
|
+
|
31
|
+
if rename_file(source_pathname, destination_pathname)
|
32
|
+
move_file(source_pathname, destination_pathname)
|
33
|
+
Excon::Response.new(status: 200)
|
34
|
+
else
|
35
|
+
fail(Excon::Errors::NotFound, '404 Not Found')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def rename_file(source, destination)
|
42
|
+
source_dir = data[source.split.first.to_s] || { files: [] }
|
43
|
+
source_file = source_dir[:files].find { |file| file['name'] == source.basename.to_s }
|
44
|
+
source_file['name'] = destination.basename.to_s if source_file
|
45
|
+
end
|
46
|
+
|
47
|
+
def move_file(source, destination)
|
48
|
+
data[destination.to_s] = data[source.to_s]
|
49
|
+
data[source.to_s] = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Akamai
|
4
|
+
class Real
|
5
|
+
# Use this action to delete an empty directory.
|
6
|
+
# @param path [String] the path to the directory
|
7
|
+
# @return [Excon::Response] response
|
8
|
+
|
9
|
+
def rmdir(path)
|
10
|
+
path_guard(path)
|
11
|
+
request(:rmdir,
|
12
|
+
method: 'POST',
|
13
|
+
path: format_path(path),
|
14
|
+
expects: 200)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Mock
|
19
|
+
def rmdir(path)
|
20
|
+
path_guard(path)
|
21
|
+
|
22
|
+
formatted_path = format_path(path)
|
23
|
+
dir = data[formatted_path]
|
24
|
+
|
25
|
+
if empty?(dir)
|
26
|
+
data.delete(formatted_path)
|
27
|
+
Excon::Response.new(status: 200)
|
28
|
+
else
|
29
|
+
fail(Excon::Errors::Conflict, 'Not a empty directory')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def empty?(dir)
|
36
|
+
dir[:files].empty? && dir[:directories].empty?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,26 +1,66 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
1
3
|
module Fog
|
2
4
|
module Storage
|
3
5
|
class Akamai
|
4
6
|
class Real
|
5
7
|
require 'fog/akamai/parsers/storage/dir'
|
6
8
|
|
9
|
+
# Use this action to check if a file or directory existis
|
10
|
+
# @param path [String] the path to check
|
11
|
+
# @return [Excon::Response] response:
|
12
|
+
# * body [Hash]:
|
13
|
+
# * directory [String] - Path of the parnt directory
|
14
|
+
# * files [Array]: - In case the stat was for a file
|
15
|
+
# * type [String]
|
16
|
+
# * name [String]
|
17
|
+
# * mtime [String]
|
18
|
+
# * size [String]
|
19
|
+
# * md5 [String]
|
20
|
+
# * directories [Array]: - In case the stat was for a directory
|
21
|
+
# * type [String]
|
22
|
+
# * name [String]
|
23
|
+
# * mtime [String]
|
24
|
+
|
7
25
|
def stat(path)
|
8
|
-
|
26
|
+
path_guard(path)
|
9
27
|
request(:stat,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
:parser => Fog::Parsers::Storage::Akamai::Dir.new
|
15
|
-
})
|
28
|
+
path: format_path(path),
|
29
|
+
method: 'GET',
|
30
|
+
expects: 200,
|
31
|
+
parser: Fog::Parsers::Storage::Akamai::Dir.new)
|
16
32
|
end
|
17
33
|
end
|
18
34
|
|
19
35
|
class Mock
|
20
36
|
def stat(path)
|
21
|
-
|
37
|
+
path_guard(path)
|
38
|
+
|
39
|
+
path = Pathname.new(format_path(path))
|
40
|
+
key = path.split.first.to_s
|
41
|
+
|
42
|
+
response = Excon::Response.new(status: get_status(key, path.basename.to_s), body: get_body(key))
|
43
|
+
|
44
|
+
fail(Excon::Errors::NotFound, '404 Not Found') if response.status == 404
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def get_status(key, basename)
|
51
|
+
if data.key?(key) &&
|
52
|
+
(data[key][:directories].any? { |dir| dir['name'] == basename } ||
|
53
|
+
data[key][:files].any? { |file| file['name'] == basename })
|
54
|
+
200
|
55
|
+
else
|
56
|
+
404
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_body(key)
|
61
|
+
data[key] if data.key?(key)
|
22
62
|
end
|
23
63
|
end
|
24
64
|
end
|
25
65
|
end
|
26
|
-
end
|
66
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Akamai
|
4
|
+
class Real
|
5
|
+
# Use this action to rename a file or symbolic link.
|
6
|
+
# @param source [String] the path to check
|
7
|
+
# @param target [String] the path to check
|
8
|
+
# @return [Excon::Response] response
|
9
|
+
|
10
|
+
def symlink(source, target)
|
11
|
+
path_guard(source)
|
12
|
+
path_guard(target)
|
13
|
+
|
14
|
+
request({ action: :symlink, target: CGI.escape(format_path(target)) },
|
15
|
+
path: format_path(source),
|
16
|
+
method: 'POST',
|
17
|
+
expects: 200)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Mock
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,27 +1,51 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
1
3
|
module Fog
|
2
4
|
module Storage
|
3
5
|
class Akamai
|
4
6
|
class Real
|
7
|
+
# Use this action to upload a file
|
8
|
+
# @param path [String] the path to where to upload
|
9
|
+
# @param body [File] the file to upload, can be file or a byte array
|
10
|
+
# @return [Excon::Response] response
|
11
|
+
|
5
12
|
def upload(path, body)
|
6
|
-
|
7
|
-
|
13
|
+
path_and_body_guard(path, body)
|
14
|
+
|
8
15
|
data = Fog::Storage.parse_data(body)
|
9
16
|
request(:upload,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:expects => 200
|
16
|
-
})
|
17
|
+
path: format_path(path),
|
18
|
+
method: 'PUT',
|
19
|
+
headers: data[:headers],
|
20
|
+
body: data[:body],
|
21
|
+
expects: 200)
|
17
22
|
end
|
18
23
|
end
|
19
24
|
|
20
25
|
class Mock
|
21
26
|
def upload(path, body)
|
22
|
-
|
27
|
+
path_and_body_guard(path, body)
|
28
|
+
|
29
|
+
path = Pathname(path)
|
30
|
+
dir = path.split.first.to_s
|
31
|
+
|
32
|
+
mkdir(dir)
|
33
|
+
add_file(dir, path, body)
|
34
|
+
|
35
|
+
Excon::Response.new(status: 200)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def add_file(dir, path, body)
|
41
|
+
data[format_path(dir)][:files] << build_file(body, path)
|
42
|
+
data[format_path(path.to_s)] = { body: body }
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_file(body, path)
|
46
|
+
{ 'type' => 'file', 'name' => path.basename.to_s, 'mtime' => DateTime.now.to_time.to_i.to_s, 'size' => body.size.to_s }
|
23
47
|
end
|
24
48
|
end
|
25
49
|
end
|
26
50
|
end
|
27
|
-
end
|
51
|
+
end
|
data/lib/fog/akamai/storage.rb
CHANGED
@@ -3,7 +3,7 @@ module Fog
|
|
3
3
|
class Akamai < Fog::Service
|
4
4
|
requires :akamai_host, :akamai_key_name, :akamai_key, :akamai_cp_code
|
5
5
|
|
6
|
-
VALID_ACTIONS = [:dir, :download, :stat, :upload]
|
6
|
+
VALID_ACTIONS = [:dir, :mkdir, :download, :stat, :upload, :delete, :du, :mtime, :rename, :rmdir, :symlink]
|
7
7
|
ACS_AUTH_DATA_HEADER = 'X-Akamai-ACS-Auth-Data'
|
8
8
|
ACS_AUTH_SIGN_HEADER = 'X-Akamai-ACS-Auth-Sign'
|
9
9
|
ACS_AUTH_ACTION_HEADER = 'X-Akamai-ACS-Action'
|
@@ -16,25 +16,75 @@ module Fog
|
|
16
16
|
|
17
17
|
request_path 'fog/akamai/requests/storage'
|
18
18
|
request :dir
|
19
|
+
request :mkdir
|
19
20
|
request :download
|
20
21
|
request :stat
|
21
22
|
request :upload
|
23
|
+
request :delete
|
24
|
+
request :du
|
25
|
+
request :mtime
|
26
|
+
request :rename
|
27
|
+
request :rmdir
|
28
|
+
request :symlink
|
22
29
|
|
23
30
|
module Helpers
|
24
31
|
def format_path(path)
|
25
32
|
["/#{akamai_cp_code}", path].reject(&:empty?).join
|
26
33
|
end
|
34
|
+
|
35
|
+
def path_and_body_guard(path, body)
|
36
|
+
path_guard(path)
|
37
|
+
fail ArgumentError('body is required') if body.nil?
|
38
|
+
end
|
39
|
+
|
40
|
+
def path_guard(path)
|
41
|
+
fail(ArgumentError, 'path needs to have a value') if path.nil? || path.empty?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module Initializer
|
46
|
+
def self.included(klass)
|
47
|
+
klass.instance_eval do
|
48
|
+
attr_reader :akamai_key, :akamai_host, :akamai_cp_code, :akamai_key_name, :scheme, :port
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def init(options)
|
53
|
+
@akamai_host = options[:akamai_host]
|
54
|
+
@akamai_key_name = options[:akamai_key_name]
|
55
|
+
@akamai_key = options[:akamai_key]
|
56
|
+
@akamai_cp_code = options[:akamai_cp_code]
|
57
|
+
|
58
|
+
@scheme = options[:scheme] || 'https'
|
59
|
+
@port = options[:port] || 443
|
60
|
+
end
|
27
61
|
end
|
28
62
|
|
29
63
|
class Mock
|
30
|
-
|
64
|
+
include Helpers
|
65
|
+
include Initializer
|
66
|
+
|
67
|
+
def self.data
|
68
|
+
@data ||= {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def initialize(options = {})
|
72
|
+
init(options)
|
73
|
+
end
|
74
|
+
|
75
|
+
def data
|
76
|
+
self.class.data
|
77
|
+
end
|
78
|
+
|
79
|
+
def reset_data
|
80
|
+
self.class.data.clear
|
31
81
|
end
|
32
82
|
end
|
33
83
|
|
34
84
|
class Real
|
35
85
|
include Helpers
|
86
|
+
include Initializer
|
36
87
|
|
37
|
-
attr_reader :akamai_key, :akamai_host, :akamai_cp_code, :akamai_key_name, :scheme, :port
|
38
88
|
# Initialize connection to Akamai
|
39
89
|
#
|
40
90
|
# ==== Notes
|
@@ -55,13 +105,7 @@ module Fog
|
|
55
105
|
# ==== Returns
|
56
106
|
# * Storage object for akamai.
|
57
107
|
def initialize(options = {})
|
58
|
-
|
59
|
-
@akamai_key_name = options[:akamai_key_name]
|
60
|
-
@akamai_key = options[:akamai_key]
|
61
|
-
@akamai_cp_code = options[:akamai_cp_code]
|
62
|
-
|
63
|
-
@scheme = options[:scheme] || 'https'
|
64
|
-
@port = options[:port] || 443
|
108
|
+
init(options)
|
65
109
|
end
|
66
110
|
|
67
111
|
def acs_auth_data
|
@@ -74,40 +118,41 @@ module Fog
|
|
74
118
|
end
|
75
119
|
|
76
120
|
def acs_action(action)
|
77
|
-
|
121
|
+
action = { action: action } if action.is_a?(Symbol)
|
122
|
+
|
123
|
+
fail(ArgumentError, "Invalid action #{action} valid actions are: #{VALID_ACTIONS}") unless VALID_ACTIONS.include?(action[:action])
|
78
124
|
|
79
|
-
"version=1
|
125
|
+
"version=1&#{action.map { |v, k| "#{v}=#{k}" }.join('&')}&format=xml"
|
80
126
|
end
|
81
127
|
|
82
128
|
def acs_auth_sign(auth_data, path, action)
|
83
129
|
data = auth_data + sign_string(path, action)
|
84
130
|
digest = OpenSSL::Digest::Digest::SHA256.new
|
85
|
-
Base64.encode64(OpenSSL::HMAC.digest(digest, akamai_key, data)).strip
|
131
|
+
Base64.encode64(OpenSSL::HMAC.digest(digest, akamai_key, data)).strip
|
86
132
|
end
|
87
133
|
|
88
134
|
private
|
89
135
|
|
90
|
-
|
91
|
-
|
136
|
+
def request(action, params)
|
137
|
+
url = "#{scheme}://#{akamai_host}:#{port}"
|
92
138
|
|
93
|
-
|
94
|
-
|
95
|
-
auth_sign = acs_auth_sign(auth_data, path, action)
|
139
|
+
path = params[:path]
|
140
|
+
auth_data = acs_auth_data
|
96
141
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
142
|
+
headers = {
|
143
|
+
ACS_AUTH_DATA_HEADER => auth_data,
|
144
|
+
ACS_AUTH_SIGN_HEADER => acs_auth_sign(auth_data, path, action),
|
145
|
+
ACS_AUTH_ACTION_HEADER => acs_action(action)
|
146
|
+
}.merge(params[:headers] || {})
|
102
147
|
|
103
|
-
|
104
|
-
|
105
|
-
|
148
|
+
params = params.merge(headers: headers)
|
149
|
+
Fog::XML::Connection.new(url).request(params)
|
150
|
+
end
|
106
151
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
152
|
+
def sign_string(path, action)
|
153
|
+
action = "x-akamai-acs-action:#{acs_action(action)}\n"
|
154
|
+
"#{path}\n#{action}"
|
155
|
+
end
|
111
156
|
end
|
112
157
|
end
|
113
158
|
end
|
data/lib/fog/akamai/version.rb
CHANGED
data/lib/fog/akamai.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fog-akamai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Calin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '1.22'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.35'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.35'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: timecop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.8.0
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 0.8.0
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: fog-core
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -146,6 +174,7 @@ extensions: []
|
|
146
174
|
extra_rdoc_files: []
|
147
175
|
files:
|
148
176
|
- ".gitignore"
|
177
|
+
- ".rubocop.yml"
|
149
178
|
- ".ruby-gemset"
|
150
179
|
- ".ruby-version"
|
151
180
|
- ".travis.yml"
|
@@ -166,9 +195,17 @@ files:
|
|
166
195
|
- lib/fog/akamai/models/storage/file.rb
|
167
196
|
- lib/fog/akamai/models/storage/files.rb
|
168
197
|
- lib/fog/akamai/parsers/storage/dir.rb
|
198
|
+
- lib/fog/akamai/parsers/storage/du.rb
|
199
|
+
- lib/fog/akamai/requests/storage/delete.rb
|
169
200
|
- lib/fog/akamai/requests/storage/dir.rb
|
170
201
|
- lib/fog/akamai/requests/storage/download.rb
|
202
|
+
- lib/fog/akamai/requests/storage/du.rb
|
203
|
+
- lib/fog/akamai/requests/storage/mkdir.rb
|
204
|
+
- lib/fog/akamai/requests/storage/mtime.rb
|
205
|
+
- lib/fog/akamai/requests/storage/rename.rb
|
206
|
+
- lib/fog/akamai/requests/storage/rmdir.rb
|
171
207
|
- lib/fog/akamai/requests/storage/stat.rb
|
208
|
+
- lib/fog/akamai/requests/storage/symlink.rb
|
172
209
|
- lib/fog/akamai/requests/storage/upload.rb
|
173
210
|
- lib/fog/akamai/storage.rb
|
174
211
|
- lib/fog/akamai/version.rb
|
@@ -192,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
229
|
version: '0'
|
193
230
|
requirements: []
|
194
231
|
rubyforge_project:
|
195
|
-
rubygems_version: 2.4.
|
232
|
+
rubygems_version: 2.4.8
|
196
233
|
signing_key:
|
197
234
|
specification_version: 4
|
198
235
|
summary: Module for 'fog' gem to support Akamai
|