fog-akamai 0.1.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|