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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rubocop.yml +20 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +17 -0
  7. data/CONTRIBUTING.md +18 -0
  8. data/CONTRIBUTORS.md +10 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.md +20 -0
  11. data/README.md +38 -0
  12. data/Rakefile +18 -0
  13. data/fog-atmos.gemspec +35 -0
  14. data/gemfiles/Gemfile.1.9.2- +8 -0
  15. data/gemfiles/Gemfile.1.9.3+ +7 -0
  16. data/lib/fog/atmos.rb +15 -0
  17. data/lib/fog/atmos/storage.rb +0 -0
  18. data/lib/fog/atmos/version.rb +5 -0
  19. data/lib/fog/storage/atmos.rb +187 -0
  20. data/lib/fog/storage/atmos/models/directories.rb +41 -0
  21. data/lib/fog/storage/atmos/models/directory.rb +46 -0
  22. data/lib/fog/storage/atmos/models/file.rb +106 -0
  23. data/lib/fog/storage/atmos/models/files.rb +71 -0
  24. data/lib/fog/storage/atmos/requests/delete_namespace.rb +17 -0
  25. data/lib/fog/storage/atmos/requests/get_namespace.rb +23 -0
  26. data/lib/fog/storage/atmos/requests/head_namespace.rb +18 -0
  27. data/lib/fog/storage/atmos/requests/post_namespace.rb +18 -0
  28. data/lib/fog/storage/atmos/requests/put_namespace.rb +18 -0
  29. data/tests/helper.rb +36 -0
  30. data/tests/helpers/collection_helper.rb +97 -0
  31. data/tests/helpers/compute/flavors_helper.rb +32 -0
  32. data/tests/helpers/compute/server_helper.rb +25 -0
  33. data/tests/helpers/compute/servers_helper.rb +10 -0
  34. data/tests/helpers/formats_helper.rb +98 -0
  35. data/tests/helpers/formats_helper_tests.rb +110 -0
  36. data/tests/helpers/mock_helper.rb +117 -0
  37. data/tests/helpers/model_helper.rb +31 -0
  38. data/tests/helpers/responds_to_helper.rb +11 -0
  39. data/tests/helpers/schema_validator_tests.rb +107 -0
  40. data/tests/helpers/succeeds_helper.rb +9 -0
  41. data/tests/models/storage/file_update_tests.rb +19 -0
  42. data/tests/models/storage/nested_directories_tests.rb +23 -0
  43. 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
@@ -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