fog-atmos 0.0.1
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 +7 -0
- data/.gitignore +23 -0
- data/.rubocop.yml +20 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/CONTRIBUTING.md +18 -0
- data/CONTRIBUTORS.md +10 -0
- data/Gemfile +4 -0
- data/LICENSE.md +20 -0
- data/README.md +38 -0
- data/Rakefile +18 -0
- data/fog-atmos.gemspec +35 -0
- data/gemfiles/Gemfile.1.9.2- +8 -0
- data/gemfiles/Gemfile.1.9.3+ +7 -0
- data/lib/fog/atmos.rb +15 -0
- data/lib/fog/atmos/storage.rb +0 -0
- data/lib/fog/atmos/version.rb +5 -0
- data/lib/fog/storage/atmos.rb +187 -0
- data/lib/fog/storage/atmos/models/directories.rb +41 -0
- data/lib/fog/storage/atmos/models/directory.rb +46 -0
- data/lib/fog/storage/atmos/models/file.rb +106 -0
- data/lib/fog/storage/atmos/models/files.rb +71 -0
- data/lib/fog/storage/atmos/requests/delete_namespace.rb +17 -0
- data/lib/fog/storage/atmos/requests/get_namespace.rb +23 -0
- data/lib/fog/storage/atmos/requests/head_namespace.rb +18 -0
- data/lib/fog/storage/atmos/requests/post_namespace.rb +18 -0
- data/lib/fog/storage/atmos/requests/put_namespace.rb +18 -0
- data/tests/helper.rb +36 -0
- data/tests/helpers/collection_helper.rb +97 -0
- data/tests/helpers/compute/flavors_helper.rb +32 -0
- data/tests/helpers/compute/server_helper.rb +25 -0
- data/tests/helpers/compute/servers_helper.rb +10 -0
- data/tests/helpers/formats_helper.rb +98 -0
- data/tests/helpers/formats_helper_tests.rb +110 -0
- data/tests/helpers/mock_helper.rb +117 -0
- data/tests/helpers/model_helper.rb +31 -0
- data/tests/helpers/responds_to_helper.rb +11 -0
- data/tests/helpers/schema_validator_tests.rb +107 -0
- data/tests/helpers/succeeds_helper.rb +9 -0
- data/tests/models/storage/file_update_tests.rb +19 -0
- data/tests/models/storage/nested_directories_tests.rb +23 -0
- metadata +213 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Directories < Fog::Collection
|
5
|
+
model Fog::Storage::Atmos::Directory
|
6
|
+
|
7
|
+
attribute :directory
|
8
|
+
|
9
|
+
def all
|
10
|
+
directory ? ns = directory.key : ns = ''
|
11
|
+
ns = ns + '/' unless ns =~ /\/$/
|
12
|
+
data = service.get_namespace(ns).body[:DirectoryList]
|
13
|
+
data = {:DirectoryEntry => []} if data.kind_of? String
|
14
|
+
data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash
|
15
|
+
dirs = data[:DirectoryEntry].select {|de| de[:FileType] == 'directory'}
|
16
|
+
dirs.each do |d|
|
17
|
+
d[:Filename] = ns + d[:Filename] if directory
|
18
|
+
d[:Filename] += '/' unless d[:Filename] =~ /\/$/
|
19
|
+
end
|
20
|
+
load(dirs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(key, _options = {})
|
24
|
+
return nil if key == '' # Root dir shouldn't be retrieved like this.
|
25
|
+
key =~ /\/$/ ? ns = key : ns = key + '/'
|
26
|
+
res = service.get_namespace ns
|
27
|
+
emc_meta = res.headers['x-emc-meta']
|
28
|
+
obj_id = emc_meta.scan(/objectid=(\w+),/).flatten[0]
|
29
|
+
new(:objectid => obj_id, :key => ns)
|
30
|
+
rescue Fog::Storage::Atmos::NotFound
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def new(attributes ={})
|
35
|
+
attributes = {:directory => directory}.merge(attributes) if directory
|
36
|
+
super(attributes)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Directory < Fog::Model
|
5
|
+
identity :key, :aliases => :Filename
|
6
|
+
attribute :objectid, :aliases => :ObjectID
|
7
|
+
|
8
|
+
def files
|
9
|
+
@files ||= begin
|
10
|
+
Fog::Storage::Atmos::Files.new(
|
11
|
+
:directory => self,
|
12
|
+
:service => service
|
13
|
+
)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def directories
|
18
|
+
@directories ||= begin
|
19
|
+
Fog::Storage::Atmos::Directories.new(
|
20
|
+
:directory => self,
|
21
|
+
:service => service
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save
|
27
|
+
self.key = attributes[:directory].key + key if attributes[:directory]
|
28
|
+
self.key = key + '/' unless key =~ /\/$/
|
29
|
+
res = service.post_namespace key
|
30
|
+
reload
|
31
|
+
end
|
32
|
+
|
33
|
+
def destroy(opts={})
|
34
|
+
if opts[:recursive]
|
35
|
+
files.each {|f| f.destroy }
|
36
|
+
directories.each do |d|
|
37
|
+
d.files.each {|f| f.destroy }
|
38
|
+
d.destroy(opts)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
service.delete_namespace key
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class File < Fog::Model
|
5
|
+
identity :key, :aliases => :Filename
|
6
|
+
|
7
|
+
attribute :content_length, :aliases => ['bytes', 'Content-Length'], :type => :integer
|
8
|
+
attribute :content_type, :aliases => ['content_type', 'Content-Type']
|
9
|
+
attribute :objectid, :aliases => :ObjectID
|
10
|
+
attribute :created_at, :aliases => :ctime
|
11
|
+
|
12
|
+
def body
|
13
|
+
attributes[:body] ||= if objectid
|
14
|
+
collection.get(identity).body
|
15
|
+
else
|
16
|
+
''
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def body=(new_body)
|
21
|
+
attributes[:body] = new_body
|
22
|
+
end
|
23
|
+
|
24
|
+
def directory
|
25
|
+
@directory
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy(target_directory_key, target_file_key, options={})
|
29
|
+
target_directory = service.directories.new(:key => target_directory_key)
|
30
|
+
target_directory.files.create(
|
31
|
+
:key => target_file_key,
|
32
|
+
:body => body
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
requires :directory, :key
|
38
|
+
service.delete_namespace([directory.key, key].join('/'))
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def meta_data
|
43
|
+
requires :directory, :key
|
44
|
+
service.get_namespace([directory.key, key].join('/') + "?metadata/system")
|
45
|
+
end
|
46
|
+
|
47
|
+
def file_size
|
48
|
+
data = meta_data
|
49
|
+
meta_data.headers["x-emc-meta"].match(/size=\d+/).to_s.gsub(/size=/,"")
|
50
|
+
end
|
51
|
+
|
52
|
+
def public=(new_public)
|
53
|
+
# NOOP - we don't need to flag files as public, getting the public URL for a file handles it.
|
54
|
+
end
|
55
|
+
|
56
|
+
# By default, expire in 5 years
|
57
|
+
def public_url(expires = (Time.now + 5 * 365 * 24 * 60 * 60))
|
58
|
+
file = directory.files.head(key)
|
59
|
+
self.objectid = if file && file.to_s.strip != "" then file.attributes['x-emc-meta'].scan(/objectid=(\w+),/).flatten[0] else nil end
|
60
|
+
if self.objectid && self.objectid.to_s.strip != ""
|
61
|
+
klass = service.ssl? ? URI::HTTPS : URI::HTTP
|
62
|
+
uri = klass.build(:host => service.host, :port => service.port.to_i, :path => "/rest/objects/#{self.objectid}" )
|
63
|
+
|
64
|
+
sb = "GET\n"
|
65
|
+
sb += uri.path.downcase + "\n"
|
66
|
+
sb += service.uid + "\n"
|
67
|
+
sb += String(expires.to_i())
|
68
|
+
|
69
|
+
signature = service.sign( sb )
|
70
|
+
uri.query = "uid=#{CGI::escape(service.uid)}&expires=#{expires.to_i()}&signature=#{CGI::escape(signature)}"
|
71
|
+
uri.to_s
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def save(options = {})
|
78
|
+
requires :body, :directory, :key
|
79
|
+
directory.kind_of?(Directory) ? ns = directory.key : ns = directory
|
80
|
+
ns += key
|
81
|
+
options[:headers] ||= {}
|
82
|
+
options[:headers]['Content-Type'] = content_type if content_type
|
83
|
+
options[:body] = body
|
84
|
+
begin
|
85
|
+
data = service.post_namespace(ns, options)
|
86
|
+
self.objectid = data.headers['location'].split('/')[-1]
|
87
|
+
rescue => error
|
88
|
+
if error.message =~ /The resource you are trying to create already exists./
|
89
|
+
data = service.put_namespace(ns, options)
|
90
|
+
else
|
91
|
+
raise error
|
92
|
+
end
|
93
|
+
end
|
94
|
+
# merge_attributes(data.headers)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def directory=(new_directory)
|
101
|
+
@directory = new_directory
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Files < Fog::Collection
|
5
|
+
attribute :directory
|
6
|
+
attribute :limit
|
7
|
+
attribute :marker
|
8
|
+
attribute :path
|
9
|
+
attribute :prefix
|
10
|
+
|
11
|
+
model Fog::Storage::Atmos::File
|
12
|
+
|
13
|
+
def all(options = {})
|
14
|
+
requires :directory
|
15
|
+
directory ? ns = directory.key : ns = ''
|
16
|
+
ns = ns + '/' unless ns =~ /\/$/
|
17
|
+
data = service.get_namespace(ns).body[:DirectoryList]
|
18
|
+
data = {:DirectoryEntry => []} if data.kind_of? String
|
19
|
+
data[:DirectoryEntry] = [data[:DirectoryEntry]] if data[:DirectoryEntry].kind_of? Hash
|
20
|
+
files = data[:DirectoryEntry].select {|de| de[:FileType] == 'regular'}
|
21
|
+
files.each do |s|
|
22
|
+
data = service.head_namespace(directory.key + s[:Filename], :parse => false)
|
23
|
+
headers = Hash[data.headers["x-emc-meta"].split(", ").map{|s|s.split("=")}]
|
24
|
+
s[:content_length] = data.headers["Content-Length"]
|
25
|
+
s[:content_type] = data.headers["Content-Type"]
|
26
|
+
s[:created_at] = headers["ctime"]
|
27
|
+
s[:directory] = directory
|
28
|
+
end
|
29
|
+
# TODO - Load additional file meta?
|
30
|
+
load(files)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(key, &block)
|
34
|
+
requires :directory
|
35
|
+
data = service.get_namespace(directory.key + key, :parse => false, &block)
|
36
|
+
file_data = data.headers.merge({
|
37
|
+
:body => data.body,
|
38
|
+
:key => key
|
39
|
+
})
|
40
|
+
new(file_data)
|
41
|
+
rescue Fog::Storage::Atmos::NotFound
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_url(key)
|
46
|
+
requires :directory
|
47
|
+
if self.directory.public_url
|
48
|
+
"#{self.directory.public_url}/#{key}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def head(key, options = {})
|
53
|
+
requires :directory
|
54
|
+
data = service.head_namespace(directory.key + key, :parse => false)
|
55
|
+
file_data = data.headers.merge({
|
56
|
+
:body => data.body,
|
57
|
+
:key => key
|
58
|
+
})
|
59
|
+
new(file_data)
|
60
|
+
rescue Fog::Storage::Atmos::NotFound
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def new(attributes = {})
|
65
|
+
requires :directory
|
66
|
+
super({ :directory => directory }.merge!(attributes))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Real
|
5
|
+
def delete_namespace(namespace = '', options = {})
|
6
|
+
options = options.reject {|key, value| value.nil?}
|
7
|
+
request({
|
8
|
+
:expects => 204,
|
9
|
+
:method => 'DELETE',
|
10
|
+
:path => "namespace/" + namespace,
|
11
|
+
:query => options
|
12
|
+
}.merge(options))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Real
|
5
|
+
def get_namespace(namespace = '', options = {}, &block)
|
6
|
+
options = options.reject {|key, value| value.nil?}
|
7
|
+
|
8
|
+
if block_given?
|
9
|
+
options[:response_block] = Proc.new
|
10
|
+
end
|
11
|
+
|
12
|
+
request({
|
13
|
+
:expects => 200,
|
14
|
+
:method => 'GET',
|
15
|
+
:path => "namespace/" + URI.escape(namespace),
|
16
|
+
:query => {},
|
17
|
+
:parse => true
|
18
|
+
}.merge(options))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Real
|
5
|
+
def head_namespace(namespace = '', options = {})
|
6
|
+
options = options.reject {|key, value| value.nil?}
|
7
|
+
request({
|
8
|
+
:expects => 200,
|
9
|
+
:method => 'HEAD',
|
10
|
+
:path => "namespace/" + URI.escape(namespace),
|
11
|
+
:query => {},
|
12
|
+
:parse => true
|
13
|
+
}.merge(options))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Real
|
5
|
+
def post_namespace(namespace = '', options = {})
|
6
|
+
options = options.reject {|key, value| value.nil?}
|
7
|
+
request({
|
8
|
+
:expects => 201,
|
9
|
+
:method => 'POST',
|
10
|
+
:path => "namespace/" + namespace,
|
11
|
+
:query => {},
|
12
|
+
:parse => true
|
13
|
+
}.merge(options))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fog
|
2
|
+
module Storage
|
3
|
+
class Atmos
|
4
|
+
class Real
|
5
|
+
def put_namespace(namespace = '', options = {})
|
6
|
+
options = options.reject {|key, value| value.nil?}
|
7
|
+
request({
|
8
|
+
:expects => 200,
|
9
|
+
:method => 'PUT',
|
10
|
+
:path => "namespace/" + namespace,
|
11
|
+
:query => {},
|
12
|
+
:parse => true
|
13
|
+
}.merge(options))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/tests/helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'excon'
|
2
|
+
|
3
|
+
if ENV['COVERAGE']
|
4
|
+
require 'coveralls'
|
5
|
+
require 'simplecov'
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter '/spec/'
|
9
|
+
add_filter '/test/'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/fog/atmos'))
|
14
|
+
|
15
|
+
Coveralls.wear! if ENV['COVERAGE']
|
16
|
+
|
17
|
+
Excon.defaults.merge!(:debug_request => true, :debug_response => true)
|
18
|
+
|
19
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'helpers', 'mock_helper'))
|
20
|
+
|
21
|
+
# This overrides the default 600 seconds timeout during live test runs
|
22
|
+
if Fog.mocking?
|
23
|
+
FOG_TESTING_TIMEOUT = ENV['FOG_TEST_TIMEOUT'] || 2000
|
24
|
+
Fog.timeout = 2000
|
25
|
+
Fog::Logger.warning "Setting default fog timeout to #{Fog.timeout} seconds"
|
26
|
+
else
|
27
|
+
FOG_TESTING_TIMEOUT = Fog.timeout
|
28
|
+
end
|
29
|
+
|
30
|
+
def lorem_file
|
31
|
+
File.open(File.dirname(__FILE__) + '/lorem.txt', 'r')
|
32
|
+
end
|
33
|
+
|
34
|
+
def array_differences(array_a, array_b)
|
35
|
+
(array_a - array_b) | (array_b - array_a)
|
36
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
def collection_tests(collection, params = {}, mocks_implemented = true)
|
2
|
+
tests('success') do
|
3
|
+
|
4
|
+
tests("#new(#{params.inspect})").succeeds do
|
5
|
+
pending if Fog.mocking? && !mocks_implemented
|
6
|
+
collection.new(params)
|
7
|
+
end
|
8
|
+
|
9
|
+
tests("#create(#{params.inspect})").succeeds do
|
10
|
+
pending if Fog.mocking? && !mocks_implemented
|
11
|
+
@instance = collection.create(params)
|
12
|
+
end
|
13
|
+
# FIXME: work around for timing issue on AWS describe_instances mocks
|
14
|
+
|
15
|
+
if Fog.mocking? && @instance.respond_to?(:ready?)
|
16
|
+
@instance.wait_for { ready? }
|
17
|
+
end
|
18
|
+
|
19
|
+
tests("#all").succeeds do
|
20
|
+
pending if Fog.mocking? && !mocks_implemented
|
21
|
+
collection.all
|
22
|
+
end
|
23
|
+
|
24
|
+
if !Fog.mocking? || mocks_implemented
|
25
|
+
@identity = @instance.identity
|
26
|
+
end
|
27
|
+
|
28
|
+
tests("#get(#{@identity})").succeeds do
|
29
|
+
pending if Fog.mocking? && !mocks_implemented
|
30
|
+
collection.get(@identity)
|
31
|
+
end
|
32
|
+
|
33
|
+
tests('Enumerable') do
|
34
|
+
pending if Fog.mocking? && !mocks_implemented
|
35
|
+
|
36
|
+
methods = [
|
37
|
+
'all?', 'any?', 'find', 'detect', 'collect', 'map',
|
38
|
+
'find_index', 'flat_map', 'collect_concat', 'group_by',
|
39
|
+
'none?', 'one?'
|
40
|
+
]
|
41
|
+
|
42
|
+
# JRuby 1.7.5+ issue causes a SystemStackError: stack level too deep
|
43
|
+
# https://github.com/jruby/jruby/issues/1265
|
44
|
+
if RUBY_PLATFORM == "java" and JRUBY_VERSION =~ /1\.7\.[5-8]/
|
45
|
+
methods.delete('all?')
|
46
|
+
end
|
47
|
+
|
48
|
+
methods.each do |enum_method|
|
49
|
+
if collection.respond_to?(enum_method)
|
50
|
+
tests("##{enum_method}").succeeds do
|
51
|
+
block_called = false
|
52
|
+
collection.send(enum_method) {|x| block_called = true }
|
53
|
+
block_called
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
[
|
59
|
+
'max_by','min_by'
|
60
|
+
].each do |enum_method|
|
61
|
+
if collection.respond_to?(enum_method)
|
62
|
+
tests("##{enum_method}").succeeds do
|
63
|
+
block_called = false
|
64
|
+
collection.send(enum_method) {|x| block_called = true; 0 }
|
65
|
+
block_called
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
if block_given?
|
74
|
+
yield(@instance)
|
75
|
+
end
|
76
|
+
|
77
|
+
if !Fog.mocking? || mocks_implemented
|
78
|
+
@instance.destroy
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
tests('failure') do
|
83
|
+
|
84
|
+
if !Fog.mocking? || mocks_implemented
|
85
|
+
@identity = @identity.to_s
|
86
|
+
@identity = @identity.gsub(/[a-zA-Z]/) { Fog::Mock.random_letters(1) }
|
87
|
+
@identity = @identity.gsub(/\d/) { Fog::Mock.random_numbers(1) }
|
88
|
+
@identity
|
89
|
+
end
|
90
|
+
|
91
|
+
tests("#get('#{@identity}')").returns(nil) do
|
92
|
+
pending if Fog.mocking? && !mocks_implemented
|
93
|
+
collection.get(@identity)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|